Я превратил приложение для Android (нативная Java) в библиотеку с файлом .aar
. Я помещаю его внутрь App_Resources/android/libs
и пытаюсь получить доступ к классу, но получаю эту ошибку:
JS: ОШИБКА Ошибка: Uncaught (в обещании): Ошибка: java.lang.NoClassDefFoundError: Ошибка разрешения: Lcom / github / pires / obd / reader / io / AbstractGatewayService;
com.tns.Runtime.callJSMethodNative (родной метод) com.tns.Runtime.dispatchCallJSMethodNative (Runtime.java:1203) com.tns.Runtime.callJSMethodImpl (Runtime.java:1083) com.tns.Jtimetime : 1070) com.tns.Runtime.callJSMethod (Runtime.java:1050) com.tns.Runtime.callJSMethod (Runtime.java:1042) com.tns.gen.java.lang.Runnable.run (Runnable.java:15 ) android.os.Handler.handleCallback (Handler.java:751) android.os.Handler.dispatchMessage (Handler.java:95) android.os.Looper.loop (Looper.java:154) android.app.ActivityThread.main (ActivityThread.java:6165) java.lang.reflect.Method.invoke (собственный метод) com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.java:888) com.android.internal.os.ZygoteInit. main (ZygoteInit.java:778) JS: вызвано: java.lang.ClassNotFoundException: com.github.pires.ob ...
Я захожу в класс так:
declare var com: any;
declare var java: any;
declare var android: any;
export interface Item {
name: string,
text: string
}
@Component({
selector: "ns-items",
moduleId: module.id,
templateUrl: "./items.component.html"
})
export class ItemsComponent implements OnInit {
private service: any;
private stats: ObservableArray<Item>;
reader: any;
constructor() {
this.stats = new ObservableArray([]);
let ObdGatewayService = com.github.pires.obd.reader.io.ObdGatewayService;
this.service = new ObdGatewayService(app.android.foregroundActivity);
}
Мой файл AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.pires.obd.reader"
android:versionCode="13"
android:versionName="2.0-RC2">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.READ_LOGS" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_btcar"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity
android:name="com.github.pires.obd.reader.activity.MainActivity"
android:configChanges="orientation|screenSize"
android:launchMode="singleInstance"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.github.pires.obd.reader.activity.ConfigActivity"
android:launchMode="singleInstance"
android:label="@string/app_name"></activity>
<activity
android:name="com.github.pires.obd.reader.activity.TroubleCodesActivity"
android:launchMode="singleInstance"
android:label="@string/title_activity_trouble_codes"></activity>
<activity
android:name="com.github.pires.obd.reader.activity.TripListActivity"
android:launchMode="singleInstance"
android:label="@string/title_activity_trips_list"></activity>
<service
android:name="com.github.pires.obd.reader.io.ObdGatewayService"
android:launchMode="singleInstance"
android:exported="false" />
<service
android:name="com.github.pires.obd.reader.io.MockObdGatewayService"
android:launchMode="singleInstance"
android:exported="false" />
</application>
</manifest>
Мой файл build.gradle:
buildscript {
repositories {
mavenCentral()
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.1'
}
}
repositories {
mavenLocal()
mavenCentral()
google()
jcenter()
}
//apply plugin: 'com.android.application'
apply plugin: 'com.android.library'
repositories {
mavenLocal()
mavenCentral()
google()
jcenter()
}
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 14
//applicationId 'com.github.pires.obd.reader'
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath true
}
}
}
lintOptions {
abortOnError false
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-
android.txt'), 'proguard-rules.pro'
}
}
/*libraryVariants.all { variant ->
variant.outputs.all { output ->
if (outputFile != null && outputFileName.endsWith('.aar')) {
outputFileName = "${archivesBaseName}-${version}.aar"
}
}
}*/
/*compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}*/
buildToolsVersion '28.0.3'
}
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.github.pires:obd-java-api:1.0-RC16'
implementation 'org.roboguice:roboguice:3.+'
//annotationProcessor 'org.roboguice:roboblender:3.+'
compileOnly 'org.roboguice:roboblender:3.+'
implementation 'com.squareup.retrofit:retrofit:1.9.0'
}
Мой класс com.github.pires.obd.reader.io.ObdGatewayService в файл AAR:
package com.github.pires.obd.reader.io;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
import com.github.pires.obd.commands.fuel.FuelLevelCommand;
import com.github.pires.obd.commands.control.DistanceSinceCCCommand;
import com.github.pires.obd.commands.protocol.EchoOffCommand;
import com.github.pires.obd.commands.protocol.LineFeedOffCommand;
import com.github.pires.obd.commands.protocol.ObdResetCommand;
import com.github.pires.obd.commands.protocol.SelectProtocolCommand;
import com.github.pires.obd.commands.protocol.TimeoutCommand;
import com.github.pires.obd.commands.temperature.AmbientAirTemperatureCommand;
import com.github.pires.obd.enums.ObdProtocols;
import com.github.pires.obd.exceptions.UnsupportedCommandException;
import com.github.pires.obd.reader.R;
import com.github.pires.obd.reader.activity.ConfigActivity;
import com.github.pires.obd.reader.activity.MainActivity;
import com.github.pires.obd.reader.io.ObdCommandJob.ObdCommandJobState;
import com.google.inject.Inject;
import java.io.File;
import java.io.IOException;
/**
* This service is primarily responsible for establishing and maintaining a
* permanent connection between the device where the application runs and a more
* OBD Bluetooth interface.
* <p/>
* Secondarily, it will serve as a repository of ObdCommandJobs and at the same
* time the application state-machine.
*/
public class ObdGatewayService extends AbstractGatewayService {
private static final String TAG = ObdGatewayService.class.getName();
@Inject
SharedPreferences prefs;
private BluetoothDevice dev = null;
private BluetoothSocket sock = null;
public void startService() throws IOException {
Log.d(TAG, "Starting service..");
// get the remote Bluetooth device
final String remoteDevice = prefs.getString(ConfigActivity.BLUETOOTH_LIST_KEY, null);
if (remoteDevice == null || "".equals(remoteDevice)) {
Toast.makeText(ctx, getString(R.string.text_bluetooth_nodevice), Toast.LENGTH_LONG).show();
// log error
Log.e(TAG, "No Bluetooth device has been selected.");
// TODO kill this service gracefully
stopService();
throw new IOException();
} else {
final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
dev = btAdapter.getRemoteDevice(remoteDevice);
/*
* Establish Bluetooth connection
*
* Because discovery is a heavyweight procedure for the Bluetooth adapter,
* this method should always be called before attempting to connect to a
* remote device with connect(). Discovery is not managed by the Activity,
* but is run as a system service, so an application should always call
* cancel discovery even if it did not directly request a discovery, just to
* be sure. If Bluetooth state is not STATE_ON, this API will return false.
*
* see
* http://developer.android.com/reference/android/bluetooth/BluetoothAdapter
* .html#cancelDiscovery()
*/
Log.d(TAG, "Stopping Bluetooth discovery.");
btAdapter.cancelDiscovery();
showNotification(getString(R.string.notification_action), getString(R.string.service_starting), R.drawable.ic_btcar, true, true, false);
try {
startObdConnection();
} catch (Exception e) {
Log.e(
TAG,
"There was an error while establishing connection. -> "
+ e.getMessage()
);
// in case of failure, stop this service.
stopService();
throw new IOException();
}
showNotification(getString(R.string.notification_action), getString(R.string.service_started), R.drawable.ic_btcar, true, true, false);
}
}
/**
* Start and configure the connection to the OBD interface.
* <p/>
* See http://stackoverflow.com/questions/18657427/ioexception-read-failed-socket-might-closed-bluetooth-on-android-4-3/18786701#18786701
*
* @throws IOException
*/
private void startObdConnection() throws IOException {
Log.d(TAG, "Starting OBD connection..");
isRunning = true;
try {
sock = BluetoothManager.connect(dev);
} catch (Exception e2) {
Log.e(TAG, "There was an error while establishing Bluetooth connection. Stopping app..", e2);
stopService();
throw new IOException();
}
// Let's configure the connection.
Log.d(TAG, "Queueing jobs for connection configuration..");
queueJob(new ObdCommandJob(new ObdResetCommand()));
//Below is to give the adapter enough time to reset before sending the commands, otherwise the first startup commands could be ignored.
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
queueJob(new ObdCommandJob(new EchoOffCommand()));
/*
* Will send second-time based on tests.
*
* TODO this can be done w/o having to queue jobs by just issuing
* command.run(), command.getResult() and validate the result.
*/
queueJob(new ObdCommandJob(new EchoOffCommand()));
queueJob(new ObdCommandJob(new FuelLevelCommand()));
queueJob(new ObdCommandJob(new DistanceSinceCCCommand()));
//queueJob(new ObdCommandJob(new LineFeedOffCommand()));
//queueJob(new ObdCommandJob(new TimeoutCommand(62)));
// Get protocol from preferences
final String protocol = prefs.getString(ConfigActivity.PROTOCOLS_LIST_KEY, "AUTO");
queueJob(new ObdCommandJob(new SelectProtocolCommand(ObdProtocols.valueOf(protocol))));
// Job for returning dummy data
//queueJob(new ObdCommandJob(new AmbientAirTemperatureCommand()));
queueCounter = 0L;
Log.d(TAG, "Initialization jobs queued.");
}
/**
* This method will add a job to the queue while setting its ID to the
* internal queue counter.
*
* @param job the job to queue.
*/
@Override
public void queueJob(ObdCommandJob job) {
// This is a good place to enforce the imperial units option
job.getCommand().useImperialUnits(prefs.getBoolean(ConfigActivity.IMPERIAL_UNITS_KEY, false));
// Now we can pass it along
super.queueJob(job);
}
/**
* Runs the queue until the service is stopped
*/
protected void executeQueue() throws InterruptedException {
Log.d(TAG, "Executing queue..");
while (!Thread.currentThread().isInterrupted()) {
ObdCommandJob job = null;
try {
job = jobsQueue.take();
// log job
Log.d(TAG, "Taking job[" + job.getId() + "] from queue..");
if (job.getState().equals(ObdCommandJobState.NEW)) {
Log.d(TAG, "Job state is NEW. Run it..");
job.setState(ObdCommandJobState.RUNNING);
if (sock.isConnected()) {
job.getCommand().run(sock.getInputStream(), sock.getOutputStream());
} else {
job.setState(ObdCommandJobState.EXECUTION_ERROR);
Log.e(TAG, "Can't run command on a closed socket.");
}
} else
// log not new job
Log.e(TAG,
"Job state was not new, so it shouldn't be in queue. BUG ALERT!");
} catch (InterruptedException i) {
Thread.currentThread().interrupt();
} catch (UnsupportedCommandException u) {
if (job != null) {
job.setState(ObdCommandJobState.NOT_SUPPORTED);
}
Log.d(TAG, "Command not supported. -> " + u.getMessage());
} catch (IOException io) {
if (job != null) {
if(io.getMessage().contains("Broken pipe"))
job.setState(ObdCommandJobState.BROKEN_PIPE);
else
job.setState(ObdCommandJobState.EXECUTION_ERROR);
}
Log.e(TAG, "IO error. -> " + io.getMessage());
} catch (Exception e) {
if (job != null) {
job.setState(ObdCommandJobState.EXECUTION_ERROR);
}
Log.e(TAG, "Failed to run command. -> " + e.getMessage());
}
if (job != null) {
final ObdCommandJob job2 = job;
((MainActivity) ctx).runOnUiThread(new Runnable() {
@Override
public void run() {
((MainActivity) ctx).stateUpdate(job2);
}
});
}
}
}
/**
* Stop OBD connection and queue processing.
*/
public void stopService() {
Log.d(TAG, "Stopping service..");
notificationManager.cancel(NOTIFICATION_ID);
jobsQueue.clear();
isRunning = false;
if (sock != null)
// close socket
try {
sock.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage());
}
// kill service
stopSelf();
}
public boolean isRunning() {
return isRunning;
}
public static void saveLogcatToFile(Context context, String devemail) {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
emailIntent.setType("text/plain");
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{devemail});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "OBD2 Reader Debug Logs");
StringBuilder sb = new StringBuilder();
sb.append("\nManufacturer: ").append(Build.MANUFACTURER);
sb.append("\nModel: ").append(Build.MODEL);
sb.append("\nRelease: ").append(Build.VERSION.RELEASE);
emailIntent.putExtra(Intent.EXTRA_TEXT, sb.toString());
String fileName = "OBDReader_logcat_" + System.currentTimeMillis() + ".txt";
File sdCard = Environment.getExternalStorageDirectory();
File dir = new File(sdCard.getAbsolutePath() + File.separator + "OBD2Logs");
if (dir.mkdirs()) {
File outputFile = new File(dir, fileName);
Uri uri = Uri.fromFile(outputFile);
emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
Log.d("savingFile", "Going to save logcat to " + outputFile);
//emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(Intent.createChooser(emailIntent, "Pick an Email provider").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
try {
@SuppressWarnings("unused")
Process process = Runtime.getRuntime().exec("logcat -f " + outputFile.getAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}