I want to startActivity the class in the external AndroidLibrary..
**Error :** Unable to instantiate activity ComponentInfo{com.fsd.ddc/com.fsd.toastlib.ToastActivity}: java.lang.ClassNotFoundException: Didn't find class "com.fsd.toastlib.ToastActivity"
I have already register the activity name in the manifest file
(Main) com.fsd.ddc.AndroidManifest.xml
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name="com.fsd.toastlib.ToastActivity"> <intent-filter> <action android:name="com.fsd.toastlib.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> <activity android:name=".DDCActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".TestActivity"></activity> </application>
(Main) com.fsd.ddc.DDCActivity.java
public class DDCActivity extends AppCompatActivity { // Buffer size for file copying. While 8kb is used in this sample, you // may want to tweak it based on actual size of the secondary dex file involved. private static final int BUF_SIZE = 8 * 1024; private static final String SECONDARY_DEX_NAME = "com.fsd.toastlib.jar"; private Button mToastButton = null; private ProgressDialog mProgressDialog = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_ddc); Button btn_class = (Button) findViewById(R.id.btn_class); // Before the secondary dex file can be processed by the DexClassLoader, // it has to be first copied from asset resource to a storage location. final File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE), SECONDARY_DEX_NAME); if (!dexInternalStoragePath.exists()) { mProgressDialog = ProgressDialog.show(this, getResources().getString(R.string.diag_title), getResources().getString(R.string.diag_message), true, false); // Perform the file copying in an AsyncTask. (new PrepareDexTask()).execute(dexInternalStoragePath); } else { btn_class.setEnabled(true); } btn_class.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE); // Initialize the class loader with the secondary dex file. DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(), optimizedDexOutputPath.getAbsolutePath(), null, getClassLoader()); Class<Object> libProviderClazz = null; try { libProviderClazz = (Class<Object>) cl.loadClass("com.fsd.toastlib.ToastActivity"); // CHANGED: THIS STARTS PROPERLY YOUR ACTIVITY ONCE THE CLASS LOADED final Intent intent = new Intent(getApplicationContext(), libProviderClazz); startActivityForResult(intent, 0); } catch (ClassNotFoundException e) { // handle that Exception properly here e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } }); } // File I/O code to copy the secondary dex file from asset resource to internal storage. private boolean prepareDex(File dexInternalStoragePath) { BufferedInputStream bis = null; OutputStream dexWriter = null; try { //bis = new BufferedInputStream(getAssets().open(SECONDARY_DEX_NAME)); Log.i("customClassLoader", "test"); File extDir = Environment.getExternalStorageDirectory(); Log.i("customClassLoader", extDir.getAbsolutePath()); File dexExternalStorage = new File(extDir, SECONDARY_DEX_NAME); Log.i("customClassLoader", dexExternalStorage.getAbsolutePath()); bis = new BufferedInputStream(new FileInputStream(dexExternalStorage)); dexWriter = new BufferedOutputStream(new FileOutputStream(dexInternalStoragePath)); byte[] buf = new byte[BUF_SIZE]; int len; while((len = bis.read(buf, 0, BUF_SIZE)) > 0) { dexWriter.write(buf, 0, len); } dexWriter.close(); bis.close(); return true; } catch (IOException e) { if (dexWriter != null) { try { dexWriter.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } if (bis != null) { try { bis.close(); } catch (IOException ioe) { ioe.printStackTrace(); } } return false; } } private class PrepareDexTask extends AsyncTask<File, Void, Boolean> { @Override protected void onCancelled() { super.onCancelled(); if (mProgressDialog != null) mProgressDialog.cancel(); } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); if (mProgressDialog != null) mProgressDialog.cancel(); } @Override protected Boolean doInBackground(File... dexInternalStoragePaths) { prepareDex(dexInternalStoragePaths[0]); return null; } } }
(Library) com.fsd.toastlib.ToastActivity.java
public class ToastActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_toast); Button main = (Button) findViewById(R.id.btn_main); main.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { TextView teks = (TextView) findViewById(R.id.lib_text); teks.setText("Start"); Toast.makeText(getApplicationContext(), "Game is running", Toast.LENGTH_LONG).show(); } }); } }
How to generate jar that include the res folder, currently it only contains the java file. Then, can I access it directly like my code in ToastActivity.java, ex:
TextView teks = (TextView) findViewById(R.id.lib_text);
Code that I used to generate jar file
(Library) build.gradle
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
minSdkVersion 21
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
}
// Define some tasks which are used in the build process
task copyClasses(type: Copy) { // Copy the assembled *.class files for only the current namespace into a new directory
// get directory for current namespace (PLUGIN_NAMESPACE = 'com.example.toastlib')
String PLUGIN_NAMESPACE = "com.fsd.toastlib"
def namespacePath = PLUGIN_NAMESPACE.replaceAll("\\.","/")
// set source and destination directories
from "build/intermediates/classes/release/${namespacePath}/"
into "build/intermediates/dex/${namespacePath}/"
// exclude classes which don't have a corresponding .java entry in the source directory
def remExt = { name -> name.lastIndexOf('.').with {it != -1 ? name[0..<it] : name} }
eachFile {details ->
def thisFile = new File("${projectDir}/src/main/java/${namespacePath}/", remExt(details.name)+".java")
if (!(thisFile.exists())) {
details.exclude()
}
}
}
task assembleExternalJar << {
// Get the location of the Android SDK
ext.androidSdkDir = System.env.ANDROID_HOME
if(androidSdkDir == null) {
Properties localProps = new Properties()
localProps.load(new FileInputStream(file('local.properties')))
ext.androidSdkDir = localProps['sdk.dir']
}
// Make sure no existing jar file exists as this will cause dx to fail
new File("${buildDir}/intermediates/dex/com.fsd.toastlib.jar").delete();
// Use command line dx utility to convert *.class files into classes.dex inside jar archive
String cmdExt = Os.isFamily(Os.FAMILY_WINDOWS) ? '.bat' : ''
exec {
commandLine "${androidSdkDir}\\build-tools\\26.0.1\\dx${cmdExt}", '--dex',
"--output=${buildDir}/intermediates/dex/com.fsd.toastlib.jar",
"${buildDir}/intermediates/dex/"
}
copyJarToOutputs.execute()
}
task copyJarToOutputs(type: Copy) {
// Copy the built jar archive to the outputs folder
from 'build/intermediates/dex/'
into 'build/outputs/'
include '*.jar'
}
// Set the dependencies of the build tasks so that assembleExternalJar does a complete build
copyClasses.dependsOn(assemble)
assembleExternalJar.dependsOn(copyClasses)