Hello all. I need some information on how i can modify an existing apk and add a file to a specified path during installation.
Better...i need that installation creates a file in data/data/com.myprogram.android/myfile where com.myprogram.android is the path of program data. Is that possible???
thx
*bump* - after one year, this is exactly what i also need!
any answer to this? since i modify an existing .apk, changing the java-code is not an option. solution must be pure .apk based / via the manifest
you could do it in a few steps
1) copy the apk to somewhere safe (/mnt/sdcard)
2) uninstall the apk, maybe use PackageManager
3) unzip the apk to a folder, add your changed/new files, zip up the folder again (extension .apk). Use busybox's zip/unzip if you need
4)sign the new apk using this (i tried it it works)
5)install the new+signed apk, maybe use PackageManager
fl3xo said:
Hello all. I need some information on how i can modify an existing apk and add a file to a specified path during installation.
Better...i need that installation creates a file in data/data/com.myprogram.android/myfile where com.myprogram.android is the path of program data. Is that possible???
thx
Click to expand...
Click to collapse
The way I've done this in the past is just store whatever it is you want to add in the res/raw directory of the project, then when the program first runs, copy the raw resource wherever you want it in the tree.
Code:
// Copy the helper app from resources to an executable in our classpath
protected void CopyExtraBin() {
// check if it's already there
File helper = new File("/data/data/com.mypath.myprog/helper_app");
if (helper.exists()) {
// already there, nothing to do.
return;
}
InputStream setdbStream = getResources().openRawResource(R.raw.helper_app);
try {
byte[] bytes = new byte[setdbStream.available()];
DataInputStream dis = new DataInputStream(setdbStream);
dis.readFully(bytes);
FileOutputStream setdbOutStream = new FileOutputStream(
"/data/data/com.mypath.myprog/helper_app");
setdbOutStream.write(bytes);
setdbOutStream.close();
// set executable permissions on our helper
Process process = Runtime.getRuntime().exec("sh");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("chmod 755 /data/data/com.mypath.myprog/helper_app\n");
os.writeBytes("exit\n");
os.flush();
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
return;
}
}
Gene Poole said:
The way I've done this in the past is just store whatever it is you want to add in the res/raw directory of the project, then when the program first runs, copy the raw resource wherever you want it in the tree.
Code:
// Copy the helper app from resources to an executable in our classpath
protected void CopyExtraBin() {
// check if it's already there
File helper = new File("/data/data/com.mypath.myprog/helper_app");
if (helper.exists()) {
// already there, nothing to do.
return;
}
InputStream setdbStream = getResources().openRawResource(R.raw.helper_app);
try {
byte[] bytes = new byte[setdbStream.available()];
DataInputStream dis = new DataInputStream(setdbStream);
dis.readFully(bytes);
FileOutputStream setdbOutStream = new FileOutputStream(
"/data/data/com.mypath.myprog/helper_app");
setdbOutStream.write(bytes);
setdbOutStream.close();
// set executable permissions on our helper
Process process = Runtime.getRuntime().exec("sh");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("chmod 755 /data/data/com.mypath.myprog/helper_app\n");
os.writeBytes("exit\n");
os.flush();
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
return;
}
}
Click to expand...
Click to collapse
where to insert it?
Moved to Q&A.
mishanet said:
Gene Poole said:
The way I've done this in the past is just store whatever it is you want to add in the res/raw directory of the project, then when the program first runs, copy the raw resource wherever you want it in the tree.
Code:
// Copy the helper app from resources to an executable in our classpath
protected void CopyExtraBin() {
// check if it's already there
File helper = new File("/data/data/com.mypath.myprog/helper_app");
if (helper.exists()) {
// already there, nothing to do.
return;
}
InputStream setdbStream = getResources().openRawResource(R.raw.helper_app);
try {
byte[] bytes = new byte[setdbStream.available()];
DataInputStream dis = new DataInputStream(setdbStream);
dis.readFully(bytes);
FileOutputStream setdbOutStream = new FileOutputStream(
"/data/data/com.mypath.myprog/helper_app");
setdbOutStream.write(bytes);
setdbOutStream.close();
// set executable permissions on our helper
Process process = Runtime.getRuntime().exec("sh");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("chmod 755 /data/data/com.mypath.myprog/helper_app\n");
os.writeBytes("exit\n");
os.flush();
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
return;
}
}
Click to expand...
Click to collapse
where to insert it?
Click to expand...
Click to collapse
Yeah I have the same question. I'm wanting to add some files to /data/data/<appdirectory>/lib but since it's permissions are set to drwxr-xr-x system system I can't write to it without changing apk itself.
I tried putting this into some code and I just alot of cannot find symbol errors and DataInputStream and Toast. I'm sure It's just my limited knowledge and maybe changes in android but this example code doesn't seem to work for me.
Gene Poole said:
The way I've done this in the past is just store whatever it is you want to add in the res/raw directory of the project, then when the program first runs, copy the raw resource wherever you want it in the tree.
Code:
// Copy the helper app from resources to an executable in our classpath
protected void CopyExtraBin() {
// check if it's already there
File helper = new File("/data/data/com.mypath.myprog/helper_app");
if (helper.exists()) {
// already there, nothing to do.
return;
}
InputStream setdbStream = getResources().openRawResource(R.raw.helper_app);
try {
byte[] bytes = new byte[setdbStream.available()];
DataInputStream dis = new DataInputStream(setdbStream);
dis.readFully(bytes);
FileOutputStream setdbOutStream = new FileOutputStream(
"/data/data/com.mypath.myprog/helper_app");
setdbOutStream.write(bytes);
setdbOutStream.close();
// set executable permissions on our helper
Process process = Runtime.getRuntime().exec("sh");
DataOutputStream os = new DataOutputStream(process.getOutputStream());
os.writeBytes("chmod 755 /data/data/com.mypath.myprog/helper_app\n");
os.writeBytes("exit\n");
os.flush();
} catch (Exception e) {
Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
return;
}
}
Click to expand...
Click to collapse
Hi guys, I hope you can help me!
I have a Samsung i-9000 and i'm writing an application to enter the pin code of the SIM card if it has not already been done before.
I inserted android:sharedUserId="android.uid.system" in AndroidManifest.xml and this is the code in my Activity class:
Code:
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
try {
clazz = Class.forName(tm.getClass().getName());
m = clazz.getDeclaredMethod("getITelephony");
m.setAccessible(true);
it = (ITelephony)m.invoke(tm);
} catch(ClassNotFoundException e) {
Log.e("ClassNotFoundException", e.getMessage());
} catch(ClassCastException e) {
Log.e("ClassCastException", e.getMessage());
} catch(NoSuchMethodException e) {
Log.e("NoSuchMethodException", e.getMessage());
} catch(InvocationTargetException e) {
Log.e("InvocationTargetException", e.getMessage());
} catch(IllegalAccessException e) {
Log.e("IllegalAccessException", e.getMessage());
}
it.supplyPin(pin);
Now I know I have to sign my app with system certificate (or system key) to work with Android 2.3.3, but I don't how I can do it. I never signed an app before, especially as system app.
On google I found this for a Dream telephone:
- In the AndroidManifest.xml of your application: under the <manifest>
element add the attribute android:sharedUserId="android.uid.system".
- Export an unsigned version of your Android application using
Eclipse: right-click on the project >> Android Tools >> Export
Unsigned Application Package.
- Use <root-of-android-source-tree>/out/host/<your-host>/framework/
signapk.jar to sign your app using platform.x509.pem and platform.pk8
in <root-of-android-source-tree>/build/target/product/security
generated earlier: java -jar signapk.jar platform.x509.pem
platform.pk8 YourApp-unsigned.apk YourApp-signed.apk.
I don't understand the meaning of the last point!!!! What have I to do step by step?? Where is <root-of-android-source-tree>/out/host/<your-host>/framework/
signapk.jar file?? Where are platform.x509.pem and platform.pk8? It says "<root-of-android-source-tree>/build/target/product/security", but where is this path?
The setps above are for a Dream telephone, maybe there is something different in Samsung i9000?
I don't know if it could be useful, hower I have a rooted Samsung i-9000 with Darky 10.2 ROM, development tool: Eclipse, OS: Windows7 32bit
Thank you very much!!!!
There is a Dream system certificate made available by the manufacture somehow. Not all manufactures will make available of their system certificate. I too would like to make a system app myself. However, without having the apk signed by the handset system certificate, there is no way obtain system permissions. I was wondering if there is a way to add my own system certificate to my handset given that I have root access to the handset.
Ok so for these last three days i have been trying to get into the android game. I did the hello android tutorial and yea. that was boring lol, so i decided to try and create a program to temporarily fix the keyboard backlight issue being experienced by some ICS port users. i have only part of the code done but it does not execute at all. I am not sure whats the problem. I have writted additional pieces to this code but have not put them in the program as i want to figure out why it doesnt run before i add more and then clean it up.
Code:
package com.dri94.led;
import java.util.*;
import java.io.*;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class LEDLightActivity extends Activity {
/** Called when the activity is first created. */
@SuppressWarnings("null")
@Override
public void onCreate(Bundle savedInstanceState) {
final int SDK_INT;
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Scanner input = new Scanner(System.in);
DataOutputStream os = null;
TextView tv = new TextView(this);
tv.setText("Enter 'y' to turn on keyboard light or 'n' to turn it off");
String yOrN = input.next();
if (yOrN == "y") {
tv.setText("Enter SDK number 7 for GB devices or 14 for ICS devices. No other devices are supported at this time");
SDK_INT = input.nextInt();
if (SDK_INT == '7') {
try {
os.writeBytes("echo 255 > /sys/class/leds/keyboard-backlight/brightness\n"
+ "chmod 444 /sys/class/leds/keyboard-backlight/brightness\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else {
try {
os.writeBytes("echo 255 > /sys/class/leds/kpd_backlight_en\n"
+ "chmod 444 /sys/class/leds/kpd_backlight_en\n");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
Any logcat output? I don't have ICS so those paths aren't available on my device, but similar paths are symlinks and directories. Does your app have write permissions to kpd_backlight_en (or whatever the symlink points to)?
You have a lot of problems there, lets point some of them out,
Code:
DataOutputStream os = null;
Youre initializating your outputStream as null, wich is a problem consideering you use it for writing a file. Also there are so many easier ways to write files, for example, I propose this simple writing method
Code:
public static void WriteFile(String text, String file) {
try{
FileWriter fstream = new FileWriter(file);
BufferedWriter out = new BufferedWriter(fstream);
out.write(text);
out.close();
}catch (Exception e){
//Deal exception ;D
}
}
simple code usage will be then
Code:
WriteFile("255", "/sys/class/leds/keyboard-backlight/brightness");
Second error I found, comparing strings with "==", this
Code:
if (yOrN == "y")
is wrong, you should try with
Code:
if (yOrN.equals("y")) {
Another thing i dont understand is this
Code:
final int SDK_INT;
Why do you declare a variable as final, if you are gonna assign some value later, right here
Code:
SDK_INT = input.nextInt();
Last thing I found is also comparing an integer with string? I dont understand what you do there
Code:
if (SDK_INT == '7')
Also your way to manage user inputs would be better with a simple button (or toggleButton) for turn on/off lights, or even use SensorEventListener.
I hope I have helped you in some , just tell me if you need something. Good luck!
How should i initialize it? And thank you alot. Ima play with my code tomorrow. The last one though is comparing it with a character value. but this post helped alot. I appreciate it... Especially cause i made soooo many beginner mistakes. My professor would be disappointed
Sent from my XT862 using T
Questions or Problems Should Not Be Posted in the Development Forum
Please Post in the Correct Forums
Moving to Q&A
thanks i wasnt sure where to post this! ill remember that from now on
0. React Native Startup Process
React Native is a web front-end friendly hybrid development framework that can be divided into two parts at startup:
Running of Native Containers
Running of JavaScript code
The Native container is started in the existing architecture (the version number is less than 1.0.0). The native container can be divided into three parts:
Native container initialization
Full binding of native modules
Initialization of JSEngine
After the container is initialized, the stage is handed over to JavaScript, and the process can be divided into two parts:
Loading, parsing, and execution of JavaScript code
Construction of JS components
Finally, the JS Thread sends the calculated layout information to the Native end, calculates the Shadow Tree, and then the UI Thread performs layout and rendering.
I have drawn a diagram of the preceding steps. The following table describes the optimization direction of each step from left to right:
{
"lightbox_close": "Close",
"lightbox_next": "Next",
"lightbox_previous": "Previous",
"lightbox_error": "The requested content cannot be loaded. Please try again later.",
"lightbox_start_slideshow": "Start slideshow",
"lightbox_stop_slideshow": "Stop slideshow",
"lightbox_full_screen": "Full screen",
"lightbox_thumbnails": "Thumbnails",
"lightbox_download": "Download",
"lightbox_share": "Share",
"lightbox_zoom": "Zoom",
"lightbox_new_window": "New window",
"lightbox_toggle_sidebar": "Toggle sidebar"
}
Note: During React Native initialization, multiple tasks may be executed concurrently. Therefore, the preceding figure only shows the initialization process of React Native and does not correspond to the execution sequence of the actual code.
1. Upgrade React Native
The best way to improve the performance of React Native applications is to upgrade a major version of the RN. After the app is upgraded from 0.59 to 0.62, no performance optimization is performed on the app, and the startup time is shortened by 1/2. When React Native's new architecture is released, both startup speed and rendering speed will be greatly improved.
2. Native container initialization
Container initialization must start from the app entry file. I will select some key code to sort out the initialization process.
iOS source code analysis
1.AppDelegate.m
AppDelegate.m is the entry file of the iOS. The code is simple. The main content is as follows:
Code:
// AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 1. Initialize a method for loading jsbundle by RCTBridge.
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
// 2. Use RCTBridge to initialize an RCTRootView.
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"RN64"
initialProperties:nil];
// 3. Initializing the UIViewController
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
// 4. Assigns the value of RCTRootView to the view of UIViewController.
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
In general, looking at the entry document, it does three things:
Initialize an RCTBridge implementation method for loading jsbundle.
Use RCTBridge to initialize an RCTRootView.
Assign the value of RCTRootView to the view of UIViewController to mount the UI.
From the entry source code, we can see that all the initialization work points to RCTRootView, so let's see what RCTRootView does.
2.RCTRootView
Let's take a look at the header file of RCTRootView first. Let's just look at some of the methods we focus on:
Code:
// RCTRootView.h
@interface RCTRootView : UIView
// Initialization methods used in AppDelegate.m
- (instancetype)initWithBridge:(RCTBridge *)bridge
moduleName:(NSString *)moduleName
initialProperties:(nullable NSDictionary *)initialProperties NS_DESIGNATED_INITIALIZER;
From the header file:
RCTRootView inherits from UIView, so it is essentially a UI component;
When the RCTRootView invokes initWithBridge for initialization, an initialized RCTBridge must be transferred.
In the RCTRootView.m file, initWithBridge listens to a series of JS loading listening functions during initialization. After listening to the completion of JS Bundle file loading, it invokes AppRegistry.runApplication() in JS to start the RN application.
We find that RCTRootView.m only monitors various events of RCTBridge, but is not the core of initialization. Therefore, we need to go to the RCTBridge file.
3.RCTBridge.m
In RCTBridge.m, the initialization invoking path is long, and the full pasting source code is long. In short, the last call is (void)setUp. The core code is as follows:
Code:
- (Class)bridgeClass
{
return [RCTCxxBridge class];
}
- (void)setUp {
// Obtains the bridgeClass. The default value is RCTCxxBridge.
Class bridgeClass = self.bridgeClass;
// Initializing the RTCxxBridge
self.batchedBridge = [[bridgeClass alloc] initWithParentBridge:self];
// Starting RTCxxBridge
[self.batchedBridge start];
}
We can see that the initialization of the RCTBridge points to the RTCxxBridge.
4.RTCxxBridge.mm
RTCxxBridge is the core of React Native initialization, and I looked at some material, and it seems that RTCxxBridge used to be called RCTBatchedBridge, so it's OK to crudely treat these two classes as the same thing.
Since the start method of RTCxxBridge is called in RCTBridge, let's see what we do from the start method.
Code:
// RTCxxBridge.mm
- (void)start {
// 1. Initialize JSThread. All subsequent JS codes are executed in this thread.
_jsThread = [[NSThread alloc] initWithTarget:[self class] selector:@selector(runRunLoop) object:nil];
[_jsThread start];
// Creating a Parallel Queue
dispatch_group_t prepareBridge = dispatch_group_create();
// 2. Register all native modules.
[self registerExtraModules];
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// 3. Initializing the JSExecutorFactory Instance
std::shared_ptr<JSExecutorFactory> executorFactory;
// 4. Initializes the underlying instance, namely, _reactInstance.
dispatch_group_enter(prepareBridge);
[self ensureOnJavaScriptThread:^{
[weakSelf _initializeBridge:executorFactory];
dispatch_group_leave(prepareBridge);
}];
// 5. Loading the JS Code
dispatch_group_enter(prepareBridge);
__block NSData *sourceCode;
[self
loadSource:^(NSError *error, RCTSource *source) {
if (error) {
[weakSelf handleError:error];
}
sourceCode = source.data;
dispatch_group_leave(prepareBridge);
}
onProgress:^(RCTLoadingProgress *progressData) {
}
];
// 6. Execute JS after the native module and JS code are loaded.
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
RCTCxxBridge *strongSelf = weakSelf;
if (sourceCode && strongSelf.loading) {
[strongSelf executeSourceCode:sourceCode sync:NO];
}
});
}
The preceding code is long, which uses some knowledge of GCD multi-threading. The process is described as follows:
1. Initialize the JS thread_jsThread.
2. Register all native modules on the main thread.
3. Prepare the bridge between JS and Native and the JS running environment.
4. Create the message queue RCTMessageThread on the JS thread and initialize _reactInstance.
5. Load the JS Bundle on the JS thread.
6. Execute the JS code after all the preceding operations are complete.
In fact, all the above six points can be drilled down, but the source code content involved in this section is enough. Interested readers can explore the source code based on the reference materials and the React Native source code.
Android source code analysis
1.MainActivity.java & MainApplication.java
Like iOS, the startup process starts with the entry file. Let's look at MainActivity.java:
MainActivity inherits from ReactActivity and ReactActivity inherits from AppCompatActivity:
Code:
// MainActivity.java
public class MainActivity extends ReactActivity {
// The returned component name is the same as the registered name of the JS portal.
@Override
protected String getMainComponentName() {
return "rn_performance_demo";
}
}
Let's start with the Android entry file MainApplication.java:
Code:
// MainApplication.java
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
// Return the ReactPackage required by the app and add the modules to be loaded,
// This is where a third-party package needs to be added when a dependency package is added to a project.
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
return packages;
}
// JS bundle entry file. Set this parameter to index.js.
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
// SoLoader:Loading the C++ Underlying Library
SoLoader.init(this, /* native exopackage */ false);
}
}
The ReactApplication interface is simple and requires us to create a ReactNativeHost object:
Code:
public interface ReactApplication {
ReactNativeHost getReactNativeHost();
}
From the above analysis, we can see that everything points to the ReactNativeHost class. Let's take a look at it.
2.ReactNativeHost.java
The main task of ReactNativeHost is to create ReactInstanceManager.
Code:
public abstract class ReactNativeHost {
protected ReactInstanceManager createReactInstanceManager() {
ReactMarker.logMarker(ReactMarkerConstants.BUILD_REACT_INSTANCE_MANAGER_START);
ReactInstanceManagerBuilder builder =
ReactInstanceManager.builder()
// Application Context
.setApplication(mApplication)
// JSMainModulePath is equivalent to the JS Bundle on the application home page. It can transfer the URL to obtain the JS Bundle from the server.
// Of course, this can be used only in dev mode.
.setJSMainModulePath(getJSMainModuleName())
// Indicates whether to enable the dev mode.
.setUseDeveloperSupport(getUseDeveloperSupport())
// Redbox callback
.setRedBoxHandler(getRedBoxHandler())
.setJavaScriptExecutorFactory(getJavaScriptExecutorFactory())
.setUIImplementationProvider(getUIImplementationProvider())
.setJSIModulesPackage(getJSIModulePackage())
.setInitialLifecycleState(LifecycleState.BEFORE_CREATE);
// Add ReactPackage
for (ReactPackage reactPackage : getPackages()) {
builder.addPackage(reactPackage);
}
// Obtaining the Loading Path of the JS Bundle
String jsBundleFile = getJSBundleFile();
if (jsBundleFile != null) {
builder.setJSBundleFile(jsBundleFile);
} else {
builder.setBundleAssetName(Assertions.assertNotNull(getBundleAssetName()));
}
ReactInstanceManager reactInstanceManager = builder.build();
return reactInstanceManager;
}
}
3.ReactActivityDelegate.java
Let's go back to ReactActivity. It doesn't do anything by itself. All functions are implemented by its delegate class ReactActivityDelegate. So let's see how ReactActivityDelegate implements it.
Code:
public class ReactActivityDelegate {
protected void onCreate(Bundle savedInstanceState) {
String mainComponentName = getMainComponentName();
mReactDelegate =
new ReactDelegate(
getPlainActivity(), getReactNativeHost(), mainComponentName, getLaunchOptions()) {
@Override
protected ReactRootView createRootView() {
return ReactActivityDelegate.this.createRootView();
}
};
if (mMainComponentName != null) {
// Loading the app page
loadApp(mainComponentName);
}
}
protected void loadApp(String appKey) {
mReactDelegate.loadApp(appKey);
// SetContentView() method of Activity
getPlainActivity().setContentView(mReactDelegate.getReactRootView());
}
}
OnCreate() instantiates a ReactDelegate. Let's look at its implementation.
4.ReactDelegate.java
In ReactDelegate.java, I don't see it doing two things:
Create ReactRootView as the root view
Start the RN application by calling getReactNativeHost().getReactInstanceManager()
Code:
public class ReactDelegate {
public void loadApp(String appKey) {
if (mReactRootView != null) {
throw new IllegalStateException("Cannot loadApp while app is already running.");
}
// Create ReactRootView as the root view
mReactRootView = createRootView();
// Starting the RN Application
mReactRootView.startReactApplication(
getReactNativeHost().getReactInstanceManager(), appKey, mLaunchOptions);
}
}
Basic Startup Process The source code content involved in this section is here. Interested readers can explore the source code based on the reference materials and React Native source code.
Optimization Suggestions
For applications with React Native as the main body, the RN container needs to be initialized immediately after the app is started. There is no optimization idea. However, native-based hybrid development apps have the following advantages:
Since initialization takes the longest time, can we initialize it before entering the React Native container?
This method is very common because many H5 containers do the same. Before entering the WebView web page, create a WebView container pool and initialize the WebView in advance. After entering the H5 container, load data rendering to achieve the effect of opening the web page in seconds.
The concept of the RN container pool is very mysterious. It is actually a map. The key is the componentName of the RN page (that is, the app name transferred in AppRegistry.registerComponent(appName, Component)), and the value is an instantiated RCT RootView/ReactRootView.
After the app is started, it is initialized in advance. Before entering the RN container, it reads the container pool. If there is a matched container, it directly uses it. If there is no matched container, it is initialized again.
Write two simple cases. The following figure shows how to build an RN container pool for iOS.
Code:
@property (nonatomic, strong) NSMutableDictionary<NSString *, RCTRootView *> *rootViewRool;
// Container Pool
-(NSMutableDictionary<NSString *, RCTRootView *> *)rootViewRool {
if (!_rootViewRool) {
_rootViewRool = @{}.mutableCopy;
}
return _rootViewRool;
}
// Cache RCTRootView
-(void)cacheRootView:(NSString *)componentName path:(NSString *)path props:(NSDictionary *)props bridge:(RCTBridge *)bridge {
// initialization
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:componentName
initialProperties:props];
// The instantiation must be loaded to the bottom of the screen. Otherwise, the view rendering cannot be triggered
[[UIApplication sharedApplication].keyWindow.rootViewController.view insertSubview:rootView atIndex:0];
rootView.frame = [UIScreen mainScreen].bounds;
// Put the cached RCTRootView into the container pool
NSString *key = [NSString stringWithFormat:@"%@_%@", componentName, path];
self.rootViewRool[key] = rootView;
}
// Read Container
-(RCTRootView *)getRootView:(NSString *)componentName path:(NSString *)path props:(NSDictionary *)props bridge:(RCTBridge *)bridge {
NSString *key = [NSString stringWithFormat:@"%@_%@", componentName, path];
RCTRootView *rootView = self.rootViewRool[key];
if (rootView) {
return rootView;
}
// Back-to-back logic
return [[RCTRootView alloc] initWithBridge:bridge moduleName:componentName initialProperties:props];
}
Android builds the RN container pool as follows:
Code:
private HashMap<String, ReactRootView> rootViewPool = new HashMap<>();
// Creating a Container
private ReactRootView createRootView(String componentName, String path, Bundle props, Context context) {
ReactInstanceManager bridgeInstance = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
ReactRootView rootView = new ReactRootView(context);
if(props == null) {
props = new Bundle();
}
props.putString("path", path);
rootView.startReactApplication(bridgeInstance, componentName, props);
return rootView;
}
// Cache Container
public void cahceRootView(String componentName, String path, Bundle props, Context context) {
ReactRootView rootView = createRootView(componentName, path, props, context);
String key = componentName + "_" + path;
// Put the cached RCTRootView into the container pool.
rootViewPool.put(key, rootView);
}
// Read Container
public ReactRootView getRootView(String componentName, String path, Bundle props, Context context) {
String key = componentName + "_" + path;
ReactRootView rootView = rootViewPool.get(key);
if (rootView != null) {
rootView.setAppProperties(newProps);
rootViewPool.remove(key);
return rootView;
}
// Back-to-back logic
return createRootView(componentName, path, props, context);
}
Each RCTRootView/ReactRootView occupies a certain memory. Therefore, when to instantiate, how many containers to instantiate, how to limit the pool size, and when to clear containers need to be practiced and explored based on services.
3. Native Modules Binding
iOS source code analysis
The iOS Native Modules has three parts. The main part is the _initializeModules function in the middle:
Code:
// RCTCxxBridge.mm
- (void)start {
// Native modules returned by the moduleProvider in initWithBundleURL_moduleProvider_launchOptions when the RCTBridge is initialized
[self registerExtraModules];
// Registering All Custom Native Modules
(void)[self _initializeModules:RCTGetModuleClasses() withDispatchGroup:prepareBridge lazilyDiscovered:NO];
// Initializes all native modules that are lazily loaded. This command is invoked only when Chrome debugging is used
[self registerExtraLazyModules];
}
Let's see what the _initializeModules function does:
Code:
// RCTCxxBridge.mm
- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<Class> *)modules
withDispatchGroup:(dispatch_group_t)dispatchGroup
lazilyDiscovered:(BOOL)lazilyDiscovered
{
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
// Modules that were pre-initialized should ideally be set up before
// bridge init has finished, otherwise the caller may try to access the
// module directly rather than via `[bridge moduleForClass:]`, which won't
// trigger the lazy initialization process. If the module cannot safely be
// set up on the current thread, it will instead be async dispatched
// to the main thread to be set up in _prepareModulesWithDispatchGroup:.
(void)[moduleData instance];
}
}
_moduleSetupComplete = YES;
[self _prepareModulesWithDispatchGroup:dispatchGroup];
}
According to the comments in _initializeModules and _prepareModulesWithDispatchGroup, the iOS initializes all Native Modules in the main thread during JS Bundle loading (in the JSThead thread).
Based on the previous source code analysis, we can see that when the React Native iOS container is initialized, all Native Modules are initialized. If there are many Native Modules, the startup time of the Android RN container is affected.
Android source code analysis
For the registration of Native Modules, the mainApplication.java entry file provides clues:
Code:
// RCTCxxBridge.mm
- (NSArray<RCTModuleData *> *)_initializeModules:(NSArray<Class> *)modules
withDispatchGroup:(dispatch_group_t)dispatchGroup
lazilyDiscovered:(BOOL)lazilyDiscovered
{
for (RCTModuleData *moduleData in _moduleDataByID) {
if (moduleData.hasInstance && (!moduleData.requiresMainQueueSetup || RCTIsMainQueue())) {
// Modules that were pre-initialized should ideally be set up before
// bridge init has finished, otherwise the caller may try to access the
// module directly rather than via `[bridge moduleForClass:]`, which won't
// trigger the lazy initialization process. If the module cannot safely be
// set up on the current thread, it will instead be async dispatched
// to the main thread to be set up in _prepareModulesWithDispatchGroup:.
(void)[moduleData instance];
}
}
_moduleSetupComplete = YES;
[self _prepareModulesWithDispatchGroup:dispatchGroup];
}
Since auto link is enabled in React Native after 0.60, the installed third-party Native Modules are in PackageList. Therefore, you can obtain the modules of auto link by simply gettingPackages().
In the source code, in the ReactInstanceManager.java file, createReactContext() is run to create a ReactContext. One step is to register the registry of nativeModules.
Code:
// ReactInstanceManager.java
private ReactApplicationContext createReactContext(
JavaScriptExecutor jsExecutor,
JSBundleLoader jsBundleLoader) {
// Registering the nativeModules Registry
NativeModuleRegistry nativeModuleRegistry = processPackages(reactContext, mPackages, false);
}
According to the function invoking, we trace the processPackages() function and use a for loop to add all Native Modules in mPackages to the registry:
Code:
// ReactInstanceManager.java
private NativeModuleRegistry processPackages(
ReactApplicationContext reactContext,
List<ReactPackage> packages,
boolean checkAndUpdatePackageMembership) {
// Create JavaModule Registry Builder, which creates the JavaModule registry,
// JavaModule Registry Registers all JavaModules to Catalyst Instance
NativeModuleRegistryBuilder nativeModuleRegistryBuilder =
new NativeModuleRegistryBuilder(reactContext, this);
// Locking mPackages
// The mPackages type is List<ReactPackage>, which corresponds to packages in the MainApplication.java file
synchronized (mPackages) {
for (ReactPackage reactPackage : packages) {
try {
// Loop the ReactPackage injected into the application. The process is to add the modules to the corresponding registry
processPackage(reactPackage, nativeModuleRegistryBuilder);
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}
NativeModuleRegistry nativeModuleRegistry;
try {
// Generating the Java Module Registry
nativeModuleRegistry = nativeModuleRegistryBuilder.build();
} finally {
Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE);
ReactMarker.logMarker(BUILD_NATIVE_MODULE_REGISTRY_END);
}
return nativeModuleRegistry;
}
Finally, call processPackage() for real registration:
Code:
// ReactInstanceManager.java
private void processPackage(
ReactPackage reactPackage,
NativeModuleRegistryBuilder nativeModuleRegistryBuilder
) {
nativeModuleRegistryBuilder.processPackage(reactPackage);
}
As shown in the preceding process, full registration is performed when Android registers Native Modules. If there are a large number of Native Modules, the startup time of the Android RN container will be affected.
Optimization Suggestions
To be honest, full binding of Native Modules is unsolvable in the existing architecture: regardless of whether the native method is used or not, all native methods are initialized when the container is started. In the new RN architecture, TurboModules solves this problem (described in the next section of this article).
If you have to talk about optimization, you have another idea. Do you want to initialize all the native modules? Can I reduce the number of Native Modules? One step in the new architecture is Lean Core, which is to simplify the React Native core. Some functions/components (such as the WebView component) are removed from the main project of the RN and delivered to the community for maintenance. You can download and integrate them separately when you want to use them.
The main benefits of this approach are as follows:
The core is more streamlined, and the RN maintainer has more energy to maintain main functions.
Reduce the binding time of Native Modules and unnecessary JS loading time, and reduce the package size, which is more friendly to initialization performance. (After the RN version is upgraded to 0.62, the initialization speed is doubled, which is basically thanks to Lean Core.)
Accelerate iteration and optimize development experience.
Now that Lean Core's work is almost complete, see the official issue discussion section for more discussion. We can enjoy Lean Core's work as long as we upgrade React Native.
4. How to optimize the startup performance of the new RN architecture
The new architecture of React Native has been skipping votes for almost two years. Every time you ask about the progress, the official response is "Don't rush, don't rush, we're doing it."
I personally looked forward to it all year last year, but didn't wait for anything, so I don't care when the RN will update to version 1.0.0. Although the RN official has been doing some work, I have to say that their new architecture still has something. I have watched all the articles and videos on the new architecture in the market, so I have an overall understanding of the new architecture.
Because the new architecture has not been officially released, there must be some differences in details. The specific implementation details will be subject to the official React Native.
JSI
The full name of JSI is JavaScript Interface, a framework written in C++ that allows JS to call native methods directly instead of communicating asynchronously through Bridge.
How do I understand how JavaScript directly invokes Native? Let's take a simple example. When an API such as setTimeout document.getElementById is invoked on a browser, Native Code is directly invoked on the JavaScript side. You can verify the function on the browser console.
For example, I executed an order:
Code:
let el = document.createElement('div')
The variable el does not hold a JS object, but an object that is instantiated in C++. For the object held by el, set the following attributes:
Code:
el.setAttribute('width', 100)
In this case, JS synchronously invokes the setWidth method in C++ to change the width of the element.
The JSI in the new architecture of React Native is used for this purpose. With the JSI, we can use JS to directly obtain the reference of C++ objects (Host Objects), control the UI, and directly invoke methods of Native Modules, saving the overhead of bridge asynchronous communication.
Let's take a small example of how Java/OC uses JSI to expose synchronous invocation methods to JS.
Code:
#pragma once
#include <string>
#include <unordered_map>
#include <jsi/jsi.h>
// SampleJSIObject inherits from HostObject and represents an object exposed to JS
// For JS, JS can directly invoke the properties and methods on the object synchronously
class JSI_EXPORT SampleJSIObject : public facebook::jsi::HostObject {
public:
// The first step
// Exposes window.__SampleJSIObject to JavaScript
// This is a static function that is generally called from ObjC/Java during application initialization
static void SampleJSIObject::install(jsi::Runtime &runtime) {
runtime.global().setProperty(
runtime,
"__sampleJSIObject",
jsi::Function::createFromHostFunction(
runtime,
jsi::PropNameID::forAscii(runtime, "__SampleJSIObject"),
1,
[binding](jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count) {
// Returns the content of a call to window.__SampleJSIObject
return std::make_shared<SampleJSIObject>();
}));
}
// Similar to a getter, this method is used each time the JS accesses the object. The function is similar to a wrapper
// For example, if we call window.__sampleJSIObject.method1(), this method will be called
jsi::Value TurboModule::get(jsi::Runtime& runtime, const jsi::PropNameID& propName) {
// Invoking method name
// For example, when window.__sampleJSIObject.method1() is called, propNameUtf8 is method1
std::string propNameUtf8 = propName.utf8(runtime);
return jsi::Function::createFromHostFunction(
runtime,
propName,
argCount,
[](facebook::jsi::Runtime &rt, const facebook::jsi::Value &thisVal, const facebook::jsi::Value *args, size_t count) {
if (propNameUtf8 == 'method1') {
// Function processing logic when method1 is invoked
}
});
}
std::vector<PropNameID> getPropertyNames(Runtime& rt){
}
}
The above example is short. To learn more about JSI, read the React Native JSI Challenge article or read the source code directly.
TurboModules
According to the previous source code analysis, in the current architecture, native modules are fully loaded during native initialization. As services are iterated, the number of native modules increases, which takes a longer time.
TurboModules solves this problem all at once. In the new architecture, native modules are loaded in lazy mode. That is, the loading is initialized only when you invoke the corresponding native modules. This solves the problem that initializing full loading takes a long time.
The calling path of TurboModules is as follows:
Use JSI to create a top-level Native Modules Proxy, which is called global.__turboModuleProxy.
Access a Native Module. For example, to access the SampleTurboModule, execute require('NativeSampleTurboModule') on the JavaScript side.
In the NativeSampleTurboModule.js file, we call TurboModuleRegistry.getEnforcing() and then call global.__turboModuleProxy("SampleTurboModule").
When global.__turboModuleProxy is invoked, the Native method exposed by the JSI in step 1 is invoked. In this case, the C++ layer finds the ObjC/Java implementation through the input string "SampleTurboModule". Finally, a corresponding JSI object is returned.
Now that we have the JSI object of SampleTurboModule, we can use JavaScript to synchronously invoke the properties and methods of the JSI object.
Through the preceding steps, we can see that with TurboModules, Native Modules are loaded only when they are invoked for the first time. This completely eliminates the time required for fully loading Native Modules during initialization of the React Native container. In addition, we can use JSI to implement synchronous invoking of JS and Native, which takes less time and improves efficiency.
Summary
This document analyzes the startup process of the existing architecture of React Native from the perspective of Native and summarizes the performance optimization points of the Native layer. Finally, we briefly introduce the new architecture of React Native. In the next article, I'll explain how to start with JavaScript and optimize React Native's startup speed.
By Halogenated Hydrocarbons
Original Link: https://segmentfault.com/a/1190000039797508
is RN container same as google map?