目录
- 1.0 startServiceCommon()
- 2.0 ActivityManagerNative.getDefault()
- 3.0 ActivityManagerProxy.startService()方法
- 4.0 ActivityManagerNative.onTransact()
- 5.0 ActivityManagerService.startService()
- 6.0ActiveServices.startServiceLocked
- 7.0 ActiveServices.startServiceInnerLocked()
- 8.0 ActiveServices.bringUpServiceLocked()
- 10. startProcessLocked启动对应的进程
Service的源码路劲:base\core\java\android\app\Service.java,我们从Service.java开始入手。
Service的启动方式有两种,一种是stopService(Intent name),另一种是bindService(Intent service, ServiceConnection conn,int flags)
现在我们主要对 startService()进行源码分析。
1.来源于百度图片
从上面可以知道Service是继承于ContextWrapper,而ContextWrapper和ContextImpl继承Context抽象类,但Context中的方法实现是在ContextImpl中实现。
我们在Activity中调startService()方法是ContextWrapper中的
ContextWrapper.java
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service); 此中的mBase就是Context,但Context中的方法具体实现是在ContextImpl中
}
ContextImpl.java
在ContextImpl.java中有如下注释
/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
我们继续
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();这个只是警告,如果不是Process.SYSTEM_UID
return startServiceCommon(service, mUser);1.0 startServiceCommon
}
1.0 startServiceCommon()
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
validateServiceIntent(service);检查Service是否有效
service.prepareToLeaveProcess();
ActivityManagerNative.getDefault()其实就是ActivityManagerProxy对象,然后调用ActivityManagerProxy中的StartService()
我们详细分析【2.0 ActivityManagerNative.getDefault() 3.0 ActivityManagerProxy.startService()
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
}
}
return cn;
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
}
2.0 ActivityManagerNative.getDefault()
/**
* Retrieve the system's default/global activity manager.
*/
static public IActivityManager getDefault() {
return gDefault.get(); 这个get()方法其实就是调用了IActivityManager的create()方法,,先看Singleton中的代码
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
/**
* Cast a Binder object into an activity manager interface, generating
* a proxy if needed.
*/
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
第一次没有初始化,是null
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
第一次走这里
return new ActivityManagerProxy(obj);
}
所以说ActivityManagerNative.getDefault().startService()就是等于ActivityManagerProxy.startService()
3.0 ActivityManagerProxy.startService()方法
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
service.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeString(callingPackage);
data.writeInt(userId);
mRemote就是一个IBinder,这里通过Binder进行了跨进程通信,最后会运行到ActivityManagerNative.onTransact()中
mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
ComponentName res = ComponentName.readFromParcel(reply);
data.recycle();
reply.recycle();
return res;
}
到这了,我们不得不提ActivityManagerProxy和ActivityManagerNative之间的关系两者都实现IActivityManager接口,且ActivityManagerNative继承Binder
这里使用了Binder机制进行跨进程通信(下图只是展示大概的意思)
2.来源百度图片
ActivityManagerProxy看做是客户端,ActivityManagerNative看作是服务端(图2并没有展示出来)。由于暂时对Binder机制不是很了解,这里暂时跳过。
4.0 ActivityManagerNative.onTransact()
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
......
case START_SERVICE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder b = data.readStrongBinder();
IApplicationThread app = ApplicationThreadNative.asInterface(b);
Intent service = Intent.CREATOR.createFromParcel(data);
String resolvedType = data.readString();
String callingPackage = data.readString();
int userId = data.readInt();
ActivityManagerNative是个抽象类,并没有实现startService()方法,但是其继承者ActivityManagerService实现了StartService()
5.0 ActivityManagerService.startService()
ComponentName cn = startService(app, service, resolvedType, callingPackage, userId);
reply.writeNoException();
ComponentName.writeToParcel(cn, reply);
return true;
}
.......
}
5.0 ActivityManagerService.startService()
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"startService: " + service + " type=" + resolvedType);
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
6.0 mServices.startServiceLocked
ComponentName res = mServices.startServiceLocked(caller, service, //此处的mService是ActiveServices
resolvedType, callingPid, callingUid, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
6.0ActiveServices.startServiceLocked
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, String callingPackage, int userId)
throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
final boolean callerFg;
if (caller != null) {
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when starting service " + service);
}
callerFg = callerApp.setSchedGroup != Process.THREAD_GROUP_BG_NONINTERACTIVE;
} else {
callerFg = true;
}
检索服务信息
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg);
if (res == null) {
return null;
}
if (res.record == null) {
return new ComponentName("!", res.permission != null
? res.permission : "private to package");
}
ServiceRecord r = res.record;
检查是否存在启动此Service的User
if (!mAm.getUserManagerLocked().exists(r.userId)) {
Slog.d(TAG, "Trying to start service with non-existent user! " + r.userId);
return null;
}
//
NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked(
callingUid, r.packageName, service, service.getFlags(), null, r.userId);
if (unscheduleServiceRestartLocked(r, callingUid, false)) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "START SERVICE WHILE RESTART PENDING: " + r);
}
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
final ServiceMap smap = getServiceMap(r.userId);
boolean addToStarting = false;
下面直接看英文注释啦,,,我英文差,,懒得Google翻译出来
if (!callerFg && r.app == null && mAm.mStartedUsers.get(r.userId) != null) {
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
// If this is not coming from a foreground caller, then we may want
// to delay the start if there are already other background services
// that are starting. This is to avoid process start spam when lots
// of applications are all handling things like connectivity broadcasts.
// We only do this for cached processes, because otherwise an application
// can have assumptions about calling startService() for a service to run
// in its own process, and for that process to not be killed before the
// service is started. This is especially the case for receivers, which
// may start a service in onReceive() to do some additional work and have
// initialized some global state as part of that.
if (DEBUG_DELAYED_SERVICE) Slog.v(TAG_SERVICE, "Potential start delay of "
+ r + " in " + proc);
if (r.delayed) {
// This service is already scheduled for a delayed start; just leave
// it still waiting.
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Continuing to delay: " + r);
return r.name;
}
if (smap.mStartingBackground.size() >= mMaxStartingBackground) {
// Something else is starting, delay!
Slog.i(TAG_SERVICE, "Delaying start of: " + r);
smap.mDelayedStartList.add(r);
r.delayed = true;
return r.name;
}
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Not delaying: " + r);
addToStarting = true;
} else if (proc.curProcState >= ActivityManager.PROCESS_STATE_SERVICE) {
// We slightly loosen when we will enqueue this new service as a background
// starting service we are waiting for, to also include processes that are
// currently running other services or receivers.
addToStarting = true;
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Not delaying, but counting as bg: " + r);
} else if (DEBUG_DELAYED_STARTS) {
StringBuilder sb = new StringBuilder(128);
sb.append("Not potential delay (state=").append(proc.curProcState)
.append(' ').append(proc.adjType);
String reason = proc.makeAdjReason();
if (reason != null) {
sb.append(' ');
sb.append(reason);
}
sb.append("): ");
sb.append(r.toString());
Slog.v(TAG_SERVICE, sb.toString());
}
} else if (DEBUG_DELAYED_STARTS) {
if (callerFg) {
Slog.v(TAG_SERVICE, "Not potential delay (callerFg=" + callerFg + " uid="
+ callingUid + " pid=" + callingPid + "): " + r);
} else if (r.app != null) {
Slog.v(TAG_SERVICE, "Not potential delay (cur app=" + r.app + "): " + r);
} else {
Slog.v(TAG_SERVICE,
"Not potential delay (user " + r.userId + " not started): " + r);
}
}
7.0 startServiceInnerLocked
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}
7.0 ActiveServices.startServiceInnerLocked()
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
ProcessStats.ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity);
}
r.callStart = false;
synchronized (r.stats.getBatteryStats()) {
r.stats.startRunningLocked();
}
8.0 bringUpServiceLocked
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false);
if (error != null) {
return new ComponentName("!!", error);
}
if (r.startRequested && addToStarting) {
boolean first = smap.mStartingBackground.size() == 0;
smap.mStartingBackground.add(r);
r.startingBgTimeout = SystemClock.uptimeMillis() + BG_START_TIMEOUT;
if (DEBUG_DELAYED_SERVICE) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r, here);
} else if (DEBUG_DELAYED_STARTS) {
Slog.v(TAG_SERVICE, "Starting background (first=" + first + "): " + r);
}
if (first) {
smap.rescheduleDelayedStarts();
}
} else if (callerFg) {
smap.ensureNotStartingBackground(r);
}
return r.name;
}
8.0 ActiveServices.bringUpServiceLocked()
直接看英文吧,都写得很清楚
private final String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting) throws TransactionTooLargeException {
//Slog.i(TAG, "Bring up service:");
//r.dump(" ");
if (r.app != null && r.app.thread != null) {
sendServiceArgsLocked(r, execInFg, false);
return null;
}
if (!whileRestarting && r.restartDelay > 0) {
// If waiting for a restart, then do nothing.
return null;
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent);
// We are now bringing the service up, so no longer in the
// restarting state.
if (mRestartingServices.remove(r)) {
r.resetRestartCounter();
clearRestartingIfNeededLocked(r);
}
// Make sure this service is no longer considered delayed, we are starting it now.
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (bring up): " + r);
getServiceMap(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}
// Make sure that the user who owns this service is started. If not,
// we don't want to allow it to run.
if (mAm.mStartedUsers.get(r.userId) == null) {检查User的状态,如果user不存在就关闭服务
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": user " + r.userId + " is stopped";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
return msg;
}
// Service is now being launched, its package can't be stopped.
try {
AppGlobals.getPackageManager().setPackageStoppedState(
r.packageName, false, r.userId);
} catch (RemoteException e) {
} catch (IllegalArgumentException e) {
Slog.w(TAG, "Failed trying to unstop package "
+ r.packageName + ": " + e);
}
final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
final String procName = r.processName;
ProcessRecord app;
这里是判读是否是独立进程,如果不是就可以直接启动服务,如果是需要启动进程
if (!isolated) {
获取进程
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (DEBUG_MU) Slog.v(TAG_MU, "bringUpServiceLocked: appInfo.uid=" + r.appInfo.uid
+ " app=" + app);
if (app != null && app.thread != null) {
try {
添加相应的包名
app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
9.0 realStartServiceLocked真正启动服务
realStartServiceLocked(r, app, execInFg);
return null;
} catch (TransactionTooLargeException e) {
throw e;
} catch (RemoteException e) {
Slog.w(TAG, "Exception when starting service " + r.shortName, e);
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
} else {
// If this service runs in an isolated process, then each time
// we call startProcessLocked() we will get a new isolated
// process, starting another process if we are currently waiting
// for a previous process to come up. To deal with this, we store
// in the service any current isolated process it is running in or
// waiting to have come up.
app = r.isolatedProc;
}
// Not running -- get it started, and enqueue this service record
// to be executed when the app comes up.
if (app == null) {
10. startProcessLocked启动对应的进程
if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated, false)) == null) {
如果启动进程失败,,,
String msg = "Unable to launch app "
+ r.appInfo.packageName + "/"
+ r.appInfo.uid + " for service "
+ r.intent.getIntent() + ": process is bad";
Slog.w(TAG, msg);
bringDownServiceLocked(r);
return msg;
}
if (isolated) {
r.isolatedProc = app;
}
}
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
if (r.delayedStop) {
// Oh and hey we've already been asked to stop!
r.delayedStop = false;
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Applying delayed stop (in bring up): " + r);
stopServiceLocked(r);
}
}
return null;
}
9.0 片段代码分支 我们后面再跟进[-----请看10.8分析-----]
10. startProcessLocked启动对应的进程
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */,
null /* entryPoint */, null /* entryPointArgs */);
}
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
.......
app.gids = gids;
app.requiredAbi = requiredAbi;
app.instructionSet = instructionSet;
// Start the process. It will either succeed and return a result containing
// the PID of the new process, or else throw a RuntimeException.
添加android.app.ActivityThread
boolean isActivityProcess = (entryPoint == null);
if (entryPoint == null) entryPoint = "android.app.ActivityThread";
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
app.processName);
checkTime(startTime, "startProcess: asking zygote to start proc");
10.1 启动Process.start ()来启动android.app.ActivityThread
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
app.info.dataDir, entryPointArgs);
checkTime(startTime, "startProcess: returned from zygote!");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (app.isolated) {
mBatteryStatsService.addIsolatedUid(app.uid, app.info.uid);
}
.......
}
10.1 启动Process.start ()来启动android.app.ActivityThread
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
debugFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
上面就使用startViaZygote等一步一步的请我们传入的参数“android.app.ActivityThread”,并启动它
启动过程我们暂时忽略,直接进入ActivityThread的main()方法。
10.2 ActivityThread.main()
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
AndroidKeyStoreProvider.install();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper(); 初始化UI线程的Looper
创建ActivityThread,这里是ResourceManager的初始化
ActivityThread thread = new ActivityThread();
10.3 ActivityThread.attach()
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); 开启Looper循环,,很熟悉吧
throw new RuntimeException("Main thread loop unexpectedly exited");
}
10.3 ActivityThread.attach()
private void attach(boolean system) {
sCurrentActivityThread = this;
mSystemThread = system; //false
if (!system) {
ViewRootImpl.addFirstDrawHandler(new Runnable() {
@Override
public void run() {
ensureJitEnabled();
}
});
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
RuntimeInit.setApplicationObject(mAppThread.asBinder());
获取的又是ActivityManagerProxy对象
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread); 10.4 ActivityManagerProxy.attachApplication,但最终还是跑到了ActivityManagerService中attachApplication中
} catch (RemoteException ex) {
// Ignore
}
// Watch for getting close to heap limit.
BinderInternal.addGcWatcher(new Runnable() {
@Override public void run() {
if (!mSomeActivitiesChanged) {
return;
}
Runtime runtime = Runtime.getRuntime();
long dalvikMax = runtime.maxMemory();
long dalvikUsed = runtime.totalMemory() - runtime.freeMemory();
if (dalvikUsed > ((3*dalvikMax)/4)) {
if (DEBUG_MEMORY_TRIM) Slog.d(TAG, "Dalvik max=" + (dalvikMax/1024)
+ " total=" + (runtime.totalMemory()/1024)
+ " used=" + (dalvikUsed/1024));
mSomeActivitiesChanged = false;
try {
mgr.releaseSomeActivities(mAppThread);
} catch (RemoteException e) {
}
}
}
});
} else {
// Don't set application object here -- if the system crashes,
// we can't display an alert, we just want to die die die.
android.ddm.DdmHandleAppName.setAppName("system_process",
UserHandle.myUserId());
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
这里就是开始启动Application了,所以每个应用第一启动的就是Application中的代码
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate Application():" + e.toString(), e);
}
}
// add dropbox logging to libcore
DropBox.setReporter(new DropBoxReporter());
ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
@Override
public void onConfigurationChanged(Configuration newConfig) {
synchronized (mResourcesManager) {
// We need to apply this change to the resources
// immediately, because upon returning the view
// hierarchy will be informed about it.
mResourcesManager的初始化是在ActivityThread()中
if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
// This actually changed the resources! Tell
// everyone about it.
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(newConfig)) {
mPendingConfiguration = newConfig;
sendMessage(H.CONFIGURATION_CHANGED, newConfig);
}
}
}
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int level) {
}
});
}
由于篇幅太长了,因此分开,后续文章继续讲解。
历史上的今天
暂无评论...
随机推荐
食指:相信未来
当蜘蛛网无情地查封了我的炉台当灰烬的余烟叹息着贫困的悲哀我依然固执地铺平失望的灰烬用美丽的雪花写下:相信未来 当我的紫葡萄化为深秋的露水当我的鲜花依偎在别人的情怀我依然固执地用凝霜的枯藤在凄凉的大地上写下:相信未来 我要用手指那涌向天边的排浪我要用手掌那托...
Android Switch简介
前言Android中的Switch控件很使用,比如WiFi开关等。Switch可以提供切图,也可以自定义属性。本文就是主要介绍自定义。记录一下,方便自己查阅和回顾。正文activity_main.xml<Switch style="@style/Widget.AppComp...
Android监听Home键和Back键的简介
前言Home键和BACK是Android中最实用的,因此本文就简单的介绍一下监听HOME和BACK键。好记性不如烂笔头正文由于Android的管控越来越严格,HOME键已经无法拦截,但可以监听,BACK键的监听和拦截都可以。监听HOME键目前监听HOME的按键,可以监听:public...
梁文道:在铁路上开餐
在日本坐火车旅行,其中一个乐趣是可以吃到美味的铁路便当。别小看这些并非现做因而盛放在保温器皿里的食物,它们可都经过精心配制,虽经水汽持续蒸腾,但风味别具。而且每个地方都有自己的特色,例如“明石便当”,一个小陶瓮里装着炖煮得软熟耐嚼的章鱼饭,光是外形就已经可爱了。讲究点的,还可以在各个车站百货公司里搜...
Google Nexus 7 adb devices时出现 ???????????? no permissions
我在使用Nexus 7平板时,在使用adb devices后出现如下:List of devices attached???????????? no permissions同时在DDMS中显示设备名也显示????????????,也无法显示进程名,无法查看log。解决方法如下:...
Android动画之TranslateAnimation使用
Android动画View Animation 视图动画(Tween Animation 补间动画),只能用来设置View的动画Drawable Animation 帧动画(Frame动画),一帧帧地显示资源文件中的DrawableProperty Animation 属性动画,在andr...