Android开发中的若干安全问题之Activity篇

1. 本app内部使用的activity一定要设置为非公开

创新互联-专业网站定制、快速模板网站建设、高性价比嘉定网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式嘉定网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖嘉定地区。费用合理售后完善,十多年实体公司更值得信赖。

不准备对外公开的activity一定要设置为非公开,以防止被人非法调用

 
 
  1. android:name=".PrivateActivity" 
  2. android:label="@string/app_name" 
  3. android:exported="false" /> 
  4.  
  5. android:name=".PrivateActivity" 
  6. android:label="@string/app_name" 
  7. android:exported="false" /> 

 

同时,一定要注意的是, 非公开的Activity不能设置intent-filter

因为,如果假设在同一机器上,有另外一个app有同样的intent-filter的话, 调用该Activity的intent会唤醒android的选择画面, 让你选择使用那个app接受该intent。这样就会事实上绕过了非公开的设置。

2. 不要指定taskAffinity

Android中的activity全都归属于task管理 , 简单说来task是一种stack的数据结构, 先入后出。

一般来说, 如果不指明归属于什么task, 同一个app内部的所有Activity都会存续在一个task中,task的名字就是app的packageName。

因为在同一个andorid设备中,不会有两个同packageName的app存在,所以能保证Activity不被攻击。

但是如果你指明taskAffinity,比如如下

 
 
  1. [html] 
  2.  
  3. android:taskAffinity="com.winuxxan.task" 
  4. android:label="@string/app_name"> 
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14.  
  15. android:taskAffinity="com.winuxxan.task" 
  16. android:label="@string/app_name"> 
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  

 

那此时,恶意软件中的Activity如果也声明为同样的taskAffinity,那他的Activity就会启动到你的task中,就会有机会拿到你的intent

3. 不要指定LaunchMode(默认standard模式)

Android中Activity的LaunchMode分成 以下四种

Standard: 这种方式打开的Activity不会被当作rootActivity,会生成一个新的Activity的instance,会和打开者在同一个task内

singleTop: 和standard基本一样,唯一的区别在于如果当前task第一个Activity就是该Activity的话,就不会生成新的instance

singleTask:系统会创建一个新task(如果没有启动应用)和一个activity新实例在新task根部,然后,如果activity实例已经存在单独的task中,系统会调用已经存在activity的 onNewIntent()方法,而不是存在新实例,仅有一个activity实例同时存在。

singleInstance: 和singleTask相似,除了系统不会让其他的activities运行在所有持有的task实例中,这个activity是独立的,并且task中的成员只有它,任何其他activities运行这个activity都将打开一个独立的task。

所有发送给root Activity(根Activiy)的intent都会在android中留下履历。所以一般来说严禁用singleTask或者singleInstance来启动画面。

然而,即使用了standard来打开画面,也可能会出问题,比如如果调用者的Activity是用singleInstance模式打开,即使用standard模式打开被调用Activity,因为调用者的Activitytask是不能有其他task的, 所以android会被迫生成一个新的task,并且把被调用者塞进去,最后被调用者就成了rootActivity。

程序如下:

 
 
  1. AndroidManifest.xml 
  2.  
  3. [html] 
  4.  
  5. package="org.jssec.android.activity.privateactivity" 
  6. android:versionCode="1" 
  7. android:versionName="1.0" > 
  8.  
  9. android:icon="@drawable/ic_launcher" 
  10. android:label="@string/app_name" > 
  11.  
  12.  
  13. android:name=".PrivateUserActivity" 
  14. android:label="@string/app_name" 
  15. android:launchMode="singleInstance" > 
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24. android:name=".PrivateActivity" 
  25. android:label="@string/app_name" 
  26. android:exported="false" /> 
  27.  
  28.  
  29.  
  30.  
  31. package="org.jssec.android.activity.privateactivity" 
  32. android:versionCode="1" 
  33. android:versionName="1.0" > 
  34.  
  35. android:icon="@drawable/ic_launcher" 
  36. android:label="@string/app_name" > 
  37.  
  38.  
  39. android:name=".PrivateUserActivity" 
  40. android:label="@string/app_name" 
  41. android:launchMode="singleInstance" > 
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50. android:name=".PrivateActivity" 
  51. android:label="@string/app_name" 
  52. android:exported="false" /> 
  53.  
  54.  

 

非公开Activity的代码如下:

 
 
  1. [java] 
  2. package org.jssec.android.activity.privateactivity; 
  3. import android.app.Activity; 
  4. import android.content.Intent; 
  5. import android.os.Bundle; 
  6. import android.view.View; 
  7. import android.widget.Toast; 
  8. public class PrivateActivity extends Activity { 
  9. @Override 
  10. public void onCreate(Bundle savedInstanceState) { 
  11. super.onCreate(savedInstanceState); 
  12. setContentView(R.layout.private_activity); 
  13. String param = getIntent().getStringExtra("PARAM"); 
  14. Toast.makeText(this, String.format("「%s」取得。", param), 
  15. Toast.LENGTH_LONG).show(); 
  16.  
  17. public void onReturnResultClick(View view) { 
  18. Intent intent = new Intent(); 
  19. intent.putExtra("RESULT", 机密数据"); 
  20. setResult(RESULT_OK, intent); 
  21. finish(); 
  22.  
  23. package org.jssec.android.activity.privateactivity; 
  24. import android.app.Activity; 
  25. import android.content.Intent; 
  26. import android.os.Bundle; 
  27. import android.view.View; 
  28. import android.widget.Toast; 
  29. public class PrivateActivity extends Activity { 
  30. @Override 
  31. public void onCreate(Bundle savedInstanceState) { 
  32. super.onCreate(savedInstanceState); 
  33. setContentView(R.layout.private_activity); 
  34. String param = getIntent().getStringExtra("PARAM"); 
  35. Toast.makeText(this, String.format("「%s」取得。", param), 
  36. Toast.LENGTH_LONG).show(); 
  37.  
  38. public void onReturnResultClick(View view) { 
  39. Intent intent = new Intent(); 
  40. intent.putExtra("RESULT", 机密数据"); 
  41. setResult(RESULT_OK, intent); 
  42. finish(); 

 

调用非公开Activity者,以standard模式打开

 
 
  1. [java] 
  2. package org.jssec.android.activity.privateactivity; 
  3. import android.app.Activity; 
  4. import android.content.Intent; 
  5. import android.os.Bundle; 
  6. import android.view.View; 
  7. import android.widget.Toast; 
  8. public class PrivateUserActivity extends Activity { 
  9. private static final int REQUEST_CODE = 1; 
  10.  
  11. @Override 
  12. public void onCreate(Bundle savedInstanceState) { 
  13. super.onCreate(savedInstanceState); 
  14. setContentView(R.layout.user_activity); 
  15.  
  16. public void onUseActivityClick(View view) { 
  17. // 用standard模式启动非公开Activity 
  18. Intent intent = new Intent(); 
  19. intent.setClass(this, PrivateActivity.class); 
  20. intent.putExtra("PARAM", "机密数据"); 
  21. startActivityForResult(intent, REQUEST_CODE); 
  22.  
  23. @Override 
  24. public void onActivityResult(int requestCode, int resultCode, 
  25. Intent data) { 
  26. super.onActivityResult(requestCode, resultCode, data); 
  27. if (resultCode != RESULT_OK) 
  28. return; 
  29. switch (requestCode) { 
  30. case REQUEST_CODE: 
  31. String result = data.getStringExtra("RESULT"); 
  32. break; 
  33.  
  34. package org.jssec.android.activity.privateactivity; 
  35. import android.app.Activity; 
  36. import android.content.Intent; 
  37. import android.os.Bundle; 
  38. import android.view.View; 
  39. import android.widget.Toast; 
  40. public class PrivateUserActivity extends Activity { 
  41. private static final int REQUEST_CODE = 1; 
  42.  
  43. @Override 
  44. public void onCreate(Bundle savedInstanceState) { 
  45. super.onCreate(savedInstanceState); 
  46. setContentView(R.layout.user_activity); 
  47.  
  48. public void onUseActivityClick(View view) { 
  49. // 用standard模式启动非公开Activity 
  50. Intent intent = new Intent(); 
  51. intent.setClass(this, PrivateActivity.class); 
  52. intent.putExtra("PARAM", "机密数据"); 
  53. startActivityForResult(intent, REQUEST_CODE); 
  54.  
  55. @Override 
  56. public void onActivityResult(int requestCode, int resultCode, 
  57. Intent data) { 
  58. super.onActivityResult(requestCode, resultCode, data); 
  59. if (resultCode != RESULT_OK) 
  60. return; 
  61. switch (requestCode) { 
  62. case REQUEST_CODE: 
  63. String result = data.getStringExtra("RESULT"); 
  64. break; 

 

 

4. 发给Activity的intent不要设定为FLAG_ACTIVITY_NEW_TASK

就算上面的Activity的lauchMode设置完善了, 在打开intent的时候还是能指定打开模式。

比如在intent中指明用FLAG_ACTIVITY_NEW_TASK模式的话,发现该activity不存在的话,就会强制新建一个task。如果同时设置了FLAG_ACTIVITY_MULTIPLE_TASK+ FLAG_ACTIVITY_NEW_TASK,就无论如何都会生成新的task,该Activity就会变成rootActiviy,并且intent会被留成履历

 

5. Intent中数据的加密

Activity中数据的传递都依靠intent, 很容易被攻击, 所以 就算同一个app内部传递数据, 最好还是要加密, 加密算法很多

6. 明确ActivityName发送Intent

明确Activity发送Intent,能够避免被恶意软件截取。

同一app内部的发送

 
 
  1. [java] 
  2. Intent intent = new Intent(this, PictureActivity.class); 
  3. intent.putExtra("BARCODE", barcode); 
  4. startActivity(intent); 
  5.  
  6. Intent intent = new Intent(this, PictureActivity.class); 
  7. intent.putExtra("BARCODE", barcode); 
  8. startActivity(intent); 
  9.  
  10.  
  11. 不同app内部的发送 
  12.  
  13. [java] 
  14. Intent intent = new Intent(); 
  15. intent.setClassName( 
  16. "org.jssec.android.activity.publicactivity", 
  17. "org.jssec.android.activity.publicactivity.PublicActivity"); 
  18. startActivity(intent); 
  19.  
  20. Intent intent = new Intent(); 
  21. intent.setClassName( 
  22. "org.jssec.android.activity.publicactivity", 
  23. "org.jssec.android.activity.publicactivity.PublicActivity"); 
  24. startActivity(intent); 

 

但是,要注意的是!

不是指明了packageName和ActivityName就能避免所有的问题,

如果有一个恶意软件故意做成和你发送目标同packageName, 同ActivityName, 此时的intent就会被截取

7. 跨app接受Intent时,要明确对方的身份

接受到别的app发来的intent时,要能确定对方的身份。

一个好方法是比对对方的app的hashcode。

当前,前提是调用者要用startActivityForResult(),因为只有这个方法,被调用者才能得到调用者的packageName

代码如下:

被调用的Activity

 
 
  1. [java] 
  2. package org.jssec.android.activity.exclusiveactivity; 
  3. import org.jssec.android.shared.PkgCertWhitelists; 
  4. import org.jssec.android.shared.Utils; 
  5. import android.app.Activity; 
  6. import android.content.Context; 
  7. import android.content.Intent; 
  8. import android.os.Bundle; 
  9. import android.view.View; 
  10. import android.widget.Toast; 
  11. public class ExclusiveActivity extends Activity { 
  12. // hashcode的白名单 
  13. private static PkgCertWhitelists sWhitelists = null; 
  14.  
  15. private static void buildWhitelists(Context context) { 
  16. boolean isdebug = Utils.isDebuggable(context); 
  17. sWhitelists = new PkgCertWhitelists(); 
  18. sWhitelists 
  19. .add("org.jssec.android.activity.exclusiveuser", isdebug ? 
  20. "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255" 
  21. "1F039BB5 7861C27A 3916C778 8E78CE00 690B3974 3EB8259F E2627B8D 4C0EC35A"); 
  22.  
  23. private static boolean checkPartner(Context context, String pkgname) { 
  24. if (sWhitelists == null) 
  25. buildWhitelists(context); 
  26. return sWhitelists.test(context, pkgname); 
  27.  
  28. @Override 
  29. public void onCreate(Bundle savedInstanceState) { 
  30. super.onCreate(savedInstanceState); 
  31. setContentView(R.layout.main); 
  32. // check白名单 
  33. if (!checkPartner(this, getCallingPackage())) { 
  34. Toast.makeText(this, "不是白名单内部的。", Toast.LENGTH_LONG).show(); 
  35. finish(); 
  36. return; 
  37.  
  38. public void onReturnResultClick(View view) { 
  39. Intent intent = new Intent(); 
  40. intent.putExtra("RESULT", "机密数据"); 
  41. setResult(RESULT_OK, intent); 
  42. finish(); 
  43.  
  44. package org.jssec.android.activity.exclusiveactivity; 
  45. import org.jssec.android.shared.PkgCertWhitelists; 
  46. import org.jssec.android.shared.Utils; 
  47. import android.app.Activity; 
  48. import android.content.Context; 
  49. import android.content.Intent; 
  50. import android.os.Bundle; 
  51. import android.view.View; 
  52. import android.widget.Toast; 
  53. public class ExclusiveActivity extends Activity { 
  54. // hashcode的白名单 
  55. private static PkgCertWhitelists sWhitelists = null; 
  56.  
  57. private static void buildWhitelists(Context context) { 
  58. boolean isdebug = Utils.isDebuggable(context); 
  59. sWhitelists = new PkgCertWhitelists(); 
  60. sWhitelists 
  61. .add("org.jssec.android.activity.exclusiveuser", isdebug ? 
  62. "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255" 
  63. "1F039BB5 7861C27A 3916C778 8E78CE00 690B3974 3EB8259F E2627B8D 4C0EC35A"); 
  64.  
  65. private static boolean checkPartner(Context context, String pkgname) { 
  66. if (sWhitelists == null) 
  67. buildWhitelists(context); 
  68. return sWhitelists.test(context, pkgname); 
  69.  
  70. @Override 
  71. public void onCreate(Bundle savedInstanceState) { 
  72. super.onCreate(savedInstanceState); 
  73. setContentView(R.layout.main); 
  74. // check白名单 
  75. if (!checkPartner(this, getCallingPackage())) { 
  76. Toast.makeText(this, "不是白名单内部的。", Toast.LENGTH_LONG).show(); 
  77. finish(); 
  78. return; 
  79.  
  80. public void onReturnResultClick(View view) { 
  81. Intent intent = new Intent(); 
  82. intent.putExtra("RESULT", "机密数据"); 
  83. setResult(RESULT_OK, intent); 
  84. finish(); 
  85. } [java] 
  86.  
  87.  
  88. PkgCertWhitelists.java 
  89. [java] 
  90. package org.jssec.android.shared; 
  91. import java.util.HashMap; 
  92. import java.util.Map; 
  93. import android.content.Context; 
  94. public class PkgCertWhitelists { 
  95. private Map mWhitelists = new HashMap(); 
  96.  
  97. public boolean add(String pkgname, String sha256) { 
  98. if (pkgname == null) 
  99. return false; 
  100. if (sha256 == null) 
  101. return false; 
  102. sha256 = sha256.replaceAll(" ", ""); 
  103. if (sha256.length() != 64) 
  104. return false; 
  105. sha256 = sha256.toUpperCase(); 
  106. if (sha256.replaceAll("[0-9A-F]+", "").length() != 0) 
  107. return false; 
  108. mWhitelists.put(pkgname, sha256); 
  109. return true; 
  110.  
  111. public boolean test(Context ctx, String pkgname) { 
  112. String correctHash = mWhitelists.get(pkgname); 
  113. return PkgCert.test(ctx, pkgname, correctHash); 
  114.  
  115. package org.jssec.android.shared; 
  116. import java.util.HashMap; 
  117. import java.util.Map; 
  118. import android.content.Context; 
  119. public class PkgCertWhitelists { 
  120. private Map mWhitelists = new HashMap(); 
  121.  
  122. public boolean add(String pkgname, String sha256) { 
  123. if (pkgname == null) 
  124. return false; 
  125. if (sha256 == null) 
  126. return false; 
  127. sha256 = sha256.replaceAll(" ", ""); 
  128. if (sha256.length() != 64) 
  129. return false; 
  130. sha256 = sha256.toUpperCase(); 
  131. if (sha256.replaceAll("[0-9A-F]+", "").length() != 0) 
  132. return false; 
  133. mWhitelists.put(pkgname, sha256); 
  134. return true; 
  135.  
  136. public boolean test(Context ctx, String pkgname) { 
  137. String correctHash = mWhitelists.get(pkgname); 
  138. return PkgCert.test(ctx, pkgname, correctHash); 
  139. PkgCert.java 
  140. [java] 
  141.  
  142. package org.jssec.android.shared; 
  143. import java.security.MessageDigest; 
  144. import java.security.NoSuchAlgorithmException; 
  145. import android.content.Context; 
  146. import android.content.pm.PackageInfo; 
  147. import android.content.pm.PackageManager; 
  148. import android.content.pm.PackageManager.NameNotFoundException; 
  149. import android.content.pm.Signature; 
  150. public class PkgCert { 
  151. public static boolean test(Context ctx, String pkgname, 
  152. String correctHash) { 
  153. if (correctHash == null) 
  154. return false; 
  155. correctHash = correctHash.replaceAll(" ", ""); 
  156. return correctHash.equals(hash(ctx, pkgname)); 
  157.  
  158. public static String hash(Context ctx, String pkgname) { 
  159. if (pkgname == null) 
  160. return null; 
  161. try { 
  162. PackageManager pm = ctx.getPackageManager(); 
  163. PackageInfo pkginfo = pm.getPackageInfo(pkgname, 
  164. PackageManager.GET_SIGNATURES); 
  165. if (pkginfo.signatures.length != 1) 
  166. return null; 
  167. Signature sig = pkginfo.signatures[0]; 
  168. byte[] cert = sig.toByteArray(); 
  169. byte[] sha256 = computeSha256(cert); 
  170. return byte2hex(sha256); 
  171. } catch (NameNotFoundException e) { 
  172. return null; 
  173.  
  174. private static byte[] computeSha256(byte[] data) { 
  175. try { 
  176. return MessageDigest.getInstance("SHA-256").digest(data); 
  177. } catch (NoSuchAlgorithmException e) { 
  178. return null; 
  179.  
  180. private static String byte2hex(byte[] data) { 
  181. if (data == null) 
  182. return null; 
  183. final StringBuilder hexadecimal = new StringBuilder(); 
  184. for (final byte b : data) { 
  185. hexadecimal.append(String.format("%02X", b)); 
  186. return hexadecimal.toString(); 
  187.  
  188.  
  189. package org.jssec.android.shared; 
  190. import java.security.MessageDigest; 
  191. import java.security.NoSuchAlgorithmException; 
  192. import android.content.Context; 
  193. import android.content.pm.PackageInfo; 
  194. import android.content.pm.PackageManager; 
  195. import android.content.pm.PackageManager.NameNotFoundException; 
  196. import android.content.pm.Signature; 
  197. public class PkgCert { 
  198. public static boolean test(Context ctx, String pkgname, 
  199. String correctHash) { 
  200. if (correctHash == null) 
  201. return false; 
  202. correctHash = correctHash.replaceAll(" ", ""); 
  203. return correctHash.equals(hash(ctx, pkgname)); 
  204.  
  205. public static String hash(Context ctx, String pkgname) { 
  206. if (pkgname == null) 
  207. return null; 
  208. try { 
  209. PackageManager pm = ctx.getPackageManager(); 
  210. PackageInfo pkginfo = pm.getPackageInfo(pkgname, 
  211. PackageManager.GET_SIGNATURES); 
  212. if (pkginfo.signatures.length != 1) 
  213. return null; 
  214. Signature sig = pkginfo.signatures[0]; 
  215. byte[] cert = sig.toByteArray(); 
  216. byte[] sha256 = computeSha256(cert); 
  217. return byte2hex(sha256); 
  218. } catch (NameNotFoundException e) { 
  219. return null; 
  220.  
  221. private static byte[] computeSha256(byte[] data) { 
  222. try { 
  223. return MessageDigest.getInstance("SHA-256").digest(data); 
  224. } catch (NoSuchAlgorithmException e) { 
  225. return null; 
  226.  
  227. private static String byte2hex(byte[] data) { 
  228. if (data == null) 
  229. return null; 
  230. final StringBuilder hexadecimal = new StringBuilder(); 
  231. for (final byte b : data) { 
  232. hexadecimal.append(String.format("%02X", b)); 
  233. return hexadecimal.toString(); 

 

8. 所有根Activity中的intent都能被所有app共享

所有的app,只要按照如下样子,就能取出这台手机上所有task上所有根Activity接受到的intent

 
 
  1. AndroidManifest.xml 
  2.  
  3. [html] 
  4. package="org.jssec.android.intent.maliciousactivity" 
  5. android:versionCode="1" 
  6. android:versionName="1.0" > 
  7.  
  8. android:minSdkVersion="8" 
  9. android:targetSdkVersion="15" /> 
  10.  
  11. android:icon="@drawable/ic_launcher" 
  12. android:label="@string/app_name" 
  13. android:theme="@style/AppTheme" > 
  14. android:name=".MaliciousActivity" 
  15. android:label="@string/title_activity_main" > 
  16.  
  17.  
  18.  
  19.  
  20.  
  21.  
  22.  
  23.  
  24.  
  25.  
  26.  
  27.  
  28. package="org.jssec.android.intent.maliciousactivity" 
  29. android:versionCode="1" 
  30. android:versionName="1.0" > 
  31.  
  32. android:minSdkVersion="8" 
  33. android:targetSdkVersion="15" /> 
  34.  
  35. android:icon="@drawable/ic_launcher" 
  36. android:label="@string/app_name" 
  37. android:theme="@style/AppTheme" > 
  38. android:name=".MaliciousActivity" 
  39. android:label="@string/title_activity_main" > 
  40.  
  41.  
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50.  
  51. MaliciousActivity.java 
  52.  
  53. [java] 
  54. package org.jssec.android.intent.maliciousactivity; 
  55. import java.util.List; 
  56. import android.app.Activity; 
  57. import android.app.ActivityManager; 
  58. import android.content.Intent; 
  59. import android.os.Bundle; 
  60. import android.util.Log; 
  61. public class MaliciousActivity extends Activity { 
  62. @Override 
  63. public void onCreate(Bundle savedInstanceState) { 
  64. super.onCreate(savedInstanceState); 
  65. setContentView(R.layout.malicious_activity); 
  66. ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); 
  67. List list = activityManager 
  68. .getRecentTasks(100, ActivityManager.RECENT_WITH_EXCLUDED); 
  69. for (ActivityManager.RecentTaskInfo r : list) { 
  70. Intent intent = r.baseIntent; 
  71. Log.v("baseIntent", intent.toString()); 
  72.  
  73. package org.jssec.android.intent.maliciousactivity; 
  74. import java.util.List; 
  75. import android.app.Activity; 
  76. import android.app.ActivityManager; 
  77. import android.content.Intent; 
  78. import android.os.Bundle; 
  79. import android.util.Log; 
  80. public class MaliciousActivity extends Activity { 
  81. @Override 
  82. public void onCreate(Bundle savedInstanceState) { 
  83. super.onCreate(savedInstanceState); 
  84. setContentView(R.layout.malicious_activity); 
  85. ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); 
  86. List list = activityManager 
  87. .getRecentTasks(100, ActivityManager.RECENT_WITH_EXCLUDED); 
  88. for (ActivityManager.RecentTaskInfo r : list) { 
  89. Intent intent = r.baseIntent; 
  90. Log.v("baseIntent", intent.toString()); 

 

 

9. Intent数据遗漏到LogCat的可能性

如果像如下代码,那Intent中发送的数据就会被自动写入LogCat

 
 
  1. [java] 
  2. Uri uri = Uri.parse("mailto:test@gmail.com"); 
  3. Intent intent = new Intent(Intent.ACTION_SENDTO, uri); 
  4. startActivity(intent); 
  5.  
  6. Uri uri = Uri.parse("mailto:test@gmail.com"); 
  7. Intent intent = new Intent(Intent.ACTION_SENDTO, uri); 
  8. startActivity(intent); 
  9.  
  10.  
  11. 如果像如下,就能避免 
  12.  
  13. [java] 
  14. Uri uri = Uri.parse("mailto:"); 
  15. Intent intent = new Intent(Intent.ACTION_SENDTO, uri); 
  16. intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"test@gmail.com"}); 
  17. startActivity(intent); 
  18.  
  19. Uri uri = Uri.parse("mailto:"); 
  20. Intent intent = new Intent(Intent.ACTION_SENDTO, uri); 
  21. intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"test@gmail.com"}); 
  22. startActivity(intent); 

新闻名称:Android开发中的若干安全问题之Activity篇
URL地址:http://www.shufengxianlan.com/qtweb/news33/49483.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联