Android Package Name vs. Application ID



keyword
PackageName ApplicationId

keypoint
1. PackageName 和 ApplicationId 的作用。
2. AndroidManifest.xml中package在编译中的变化。
3. 如何获取 ApplicationId 和 PackageName。
4. 修改ApplicationId需要注意的问题。


一、PackageName

1、PackageName简介
在老版本中,包名是安卓应用apk的唯一标识,安卓系统管理应用时,也是以包名索引。
包名在 AndroidManifest.xml 中定义。在一个安卓应用程序工程创建之初,包名就已经确定。包名更多的是一个编译时概念。由 Android IDE 自动生成的一些类,比如R、BuildConfig,都是以AndroidManifest.xml 中定义的包名来构造类名。譬如,包名为“com.android.helloworld”,对应生成“com.android.helloworld.R”“com.android.helloworld.BuildConfig”。在需要使用的类中,需要imort,import 语句也将保留在源代码中。


2、PackageName的作用
Manifest 中的 package 主要是做两件事:

1)生成的 R.java 、BuildConfig.java 的命名空间(包名)
比如,对于本文的例子来说就是 com.android.helloworld.R、com.android.helloworld.BuildConfig。

2)解析 Manifest 中相关类名
比如,在 manifest 文件中声明 <activity android:name=".MainActivity">,实际会被解析为 com.android.helloworld.MainActivity。


3、如何获取 packageName
BuildConfig.class.getPackage().toString()
如:
package com.android.helloworld;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("test", "package_name: " + BuildConfig.class.getPackage().toString());
    }
}




二、ApplicationId

1、Application ID简介
Application ID是您应用的唯一标识。ApplicationId是Android Studio提供的一种新的机制,默认值和AndroidManifest.xml中定义的package一致,也可以在app/build.gradle中定义,如:

defaultConfig {  
        applicationId "com.android.helloworld"  
        minSdkVersion 21  
        targetSdkVersion 23  
        versionCode 1  
        versionName "1.0"  
}  

2、ApplicationId的作用
Android引入ApplicationId的原因,概括为四个字:“代码复用”。
对于一个大的工程,修改包名的成本过高,几乎不可取。而经常会出现一个工程需要出不同版本,譬如收费版和免费版。这样就可以通过简单修改 app/build.gradle 中的 applicationId 来改变最终 apk 的包名,这样对于安卓手机来说,就是不同的app。同时,又不影响到开发编译时的代码一致性。

3、自定义ApplicationId的注意事项
修改applicationId后,前面提到的IDE自动生成的类(譬如R、BuildConfig),其包名不会被更新,仍然是 AndroidManifest.xml 中定义的包名,使用他们的类中的 import 语句也不需要更改。

修改 applicationId 之后,查看运行时进程名,会变成新的 applicationId。在程序内运行时调用Context.getPackageName(),得到的也是新的 applicationId。

官方文档 中也明确提到: “ 构建工具会在构建结束时将应用 ID 复制到 APK 的最终清单文件中。所以,如果您在构建后检查 AndroidManifest.xml 文件,package 属性发生更改就不足为奇。”
使用apktool对编译生成的apk反编译,查看 AndroidManifest.xml,会发现确实是修改成了ApplicationId的值。

为什么要在构建结束时,才将Manifest中的package修改为ApplicationId?
原因是:如果过早将 package 修改为 ApplicationId,BuildConfig.java 和 R.java 的包名会变成 applicationId,这样会导致不同 build variants 的代码在引用 BuildConfig 和 R 时,imports的包名不一致。也就导致了代码不易复用,这样就背离了应用ApplicationId的初衷。


4、如何获取 applicationId
Context.getPackageName()
//或:
BuildConfig.APPLICATION_ID
如:
package com.android.helloworld;
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("test", "application_id: " + BuildConfig.APPLICATION_ID);  // 方法1
        Log.d("test", "application_id: " + getPackageName());  // 方法2
    }
}



三、需要注意的坑

对于上文提到的自动生成的Java类(R、BuildConfig),如果不是通过正常的 import,而是通过在运行时调用 Context.getPackageName() 得到包名,然后拼接成类名,再用反射来获取类。这样就会遇到 ClassNotFoundException,所以尽量不要用这种方式。

如:
public static int getResourseIdByName(String packageName, String className,
			String name)
	{
		Class r = null;
		int id = 0;
		try
		{
			r = Class.forName(packageName + ".R");
			Class[] classes = r.getClasses();
			Class desireClass = null;
			for (int i = 0; i < classes.length; i++)
			{
				if (classes[i].getName().split("\\$")[1].equals(className))
				{
					desireClass = classes[i];
					break;
				}
			}
			if (desireClass != null)
				id = desireClass.getField(name).getInt(desireClass);
		} catch (ClassNotFoundException e)
		{
			e.printStackTrace();
		} catch (IllegalArgumentException e)
		{
			e.printStackTrace();
		} catch (SecurityException e)
		{
			e.printStackTrace();
		} catch (IllegalAccessException e)
		{
			e.printStackTrace();
		} catch (NoSuchFieldException e)
		{
			e.printStackTrace();
		}
		return id;
	}





四、参考


猜你喜欢

转载自blog.csdn.net/zhoaya188/article/details/79496935