Skip to content

Instantly share code, notes, and snippets.

@SelvinPL
Last active March 1, 2022 14:27
Show Gist options
  • Select an option

  • Save SelvinPL/eefa88add4fa33f41ca8 to your computer and use it in GitHub Desktop.

Select an option

Save SelvinPL/eefa88add4fa33f41ca8 to your computer and use it in GitHub Desktop.
Dynamic fragment loading
package pl.selvin.fragmentloader;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import dalvik.system.DexClassLoader;
import okio.BufferedSink;
import okio.Okio;
public class Activities {
public static class MainActivity extends AppCompatActivity {
private final static String[][] setupData = new String[][]{
new String[]{"http://selvin.pl/dynamicfragment.jar", "pl.selvin.dynamicfragment.Fragment1"},
new String[]{"http://selvin.pl/dynamicfragment.jar", "pl.selvin.dynamicfragment.Fragment2"},
new String[]{"http://selvin.pl/dynamicfragments2.jar", "pl.selvin.dynamicfragments2.Fragment1"}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
final View.OnClickListener buttonClick = (new View.OnClickListener() {
@Override
public void onClick(View v) {
final String[] rowData = (String[]) v.getTag();
final Intent intent = new Intent(MainActivity.this, GenericActivity.class);
intent.putExtra(GenericActivity.JAR_URL, rowData[0]);
intent.putExtra(GenericActivity.FRAGMENT_CLASS_NAME, rowData[1]);
startActivity(intent);
}
});
for (int r = 0; r < setupData.length; r++) {
final Button button = new Button(this);
layout.addView(button);
final String[] rowData = setupData[r];
button.setText(rowData[0] + "\n" + rowData[1]);
button.setTag(rowData);
button.setOnClickListener(buttonClick);
}
setContentView(layout);
}
}
public static class GenericActivity extends AppCompatActivity {
public static String JAR_URL = "JAR_URL";
public static String FRAGMENT_CLASS_NAME = "FRAGMENT_CLASS_NAME";
//there should be better way - do not use it in production
private static DexClassLoader loader;
//url that we already loaded
private static ArrayList<String> loadedUris = new ArrayList<>();
@Override
public ClassLoader getClassLoader() {
if (loader != null)
return loader;
return super.getClassLoader();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
final ProgressBar progressBar = new ProgressBar(this);
progressBar.setIndeterminate(true);
setContentView(progressBar);
new AsyncTask<Void, Void, Fragment>() {
@Override
protected Fragment doInBackground(Void... params) {
final File outputDir = getCacheDir();
try {
final String url = getIntent().getStringExtra(JAR_URL);
if (!loadedUris.contains(url)) {
final OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
if (!response.isSuccessful())
throw new IOException("Unexpected code " + response);
final File downloadedFile = File.createTempFile("temp", ".jar", outputDir);
BufferedSink sink = Okio.buffer(Okio.sink(downloadedFile));
sink.writeAll(response.body().source());
sink.close();
final File tmpDir = getDir("dex", 0);
final String libPath = downloadedFile.getPath();
loader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, getClassLoader());
loadedUris.add(url);
}
Class<?> classToLoad = getClassLoader().loadClass(getIntent().getStringExtra(FRAGMENT_CLASS_NAME));
return (Fragment) classToLoad.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(Fragment fragment) {
progressBar.setVisibility(View.GONE);
if (fragment != null) {
getSupportFragmentManager().beginTransaction().add(android.R.id.content, fragment, "frag").commit();
}
}
}.execute();
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pl.selvin.fragmentloader">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:label="FragmentLoader"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".Activities$MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Activities$GenericActivity" />
</application>
</manifest>
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "pl.selvin.fragmentloader"
minSdkVersion 10
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.squareup.okhttp:okhttp:2.5.0'
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment