深入理解 Android 中的各种 Context

前言

网上关于 Context 的文章也已经有不少了,比如值得参考的有:

Android Context完全解析,你所不知道的Context的各种细节

Android Context 到底是什么?

但看了一下,发现还有值得讨论的地方,比如这个等式:

Context个数 = Service 个数 + Activity 个数 + 1

老实说,我不明白这个等式有什么意义,而且还是错的。首先多进程情况下,Application 对象就不止一个;其次,Activity、Service、Application 继承自 ContextWrapper,它们自己就是一个 Context,里面又有一个 Base Context;最后,还有各种 outer context、display context 什么的,这部分没深入研究过,但 Context 的数量绝对大于上述等式的两倍了。

上面这部分算一个讨论,下面正式进入正题。

Context 家族

Context 本身是一个抽象类,主要实现类为 ContextImpl,另外有子类 ContextWrapper 和 ContextThemeWrapper,这两个子类都是 Context 的代理类,主要区别是 ContextThemeWrapper 有自己的主题资源。它们继承关系如下:

image

Context 有什么用?

如果要弄清楚 “某个类有什么用” 这样的问题,其实很简单,看一下它提供了什么接口就知道了,下面列举一些主要的:

/**
* Interface to global information about an application environment.  This is
* an abstract class whose implementation is provided by
* the Android system.  It
* allows access to application-specific resources and classes,as well as
* up-calls for application-level operations such as launching activities,* broadcasting and receiving intents,etc.
*/
public abstract class Context {
    
    // 四大组件相关
    public abstract void startActivity(@RequiresPermission Intent intent);
    public abstract void sendBroadcast(@RequiresPermission Intent intent);
    public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,IntentFilter filter);
    public abstract void unregisterReceiver(BroadcastReceiver receiver);
    public abstract ComponentName startService(Intent service);
    public abstract boolean stopService(Intent service);
    public abstract boolean bindService(@RequiresPermission Intent service,@NonNull ServiceConnection conn,@BindServiceFlags int flags);
    public abstract void unbindService(@NonNull ServiceConnection conn);
    public abstract ContentResolver getContentResolver();
    
    // 获取系统/应用资源
    public abstract AssetManager getAssets();
    public abstract Resources getResources();
    public abstract PackageManager getPackageManager();
    public abstract Context getApplicationContext();
    public abstract ClassLoader getClassLoader();
    public final @Nullable <T> T getSystemService(@NonNull Class<T> serviceClass) { ... }
    
    public final String getString(@StringRes int resId) { ... }
    public final int getColor(@ColorRes int id) { ... }
    public final Drawable getDrawable(@DrawableRes int id) { ... }
    public abstract Resources.Theme getTheme();
    public abstract void setTheme(@StyleRes int resid);
    public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) { ... }
    
    // 获取应用相关信息
    public abstract ApplicationInfo getApplicationInfo();
    public abstract String getPackageName();
    public abstract Looper getMainLooper();
    public abstract int checkPermission(@NonNull String permission,int pid,int uid);
    
    // 文件相关
    public abstract File getSharedPreferencesPath(String name);
    public abstract File getDataDir();
    public abstract boolean deleteFile(String name);
    public abstract File getExternalFilesDir(@Nullable String type);
    public abstract File getCacheDir();
    ...
    public abstract SharedPreferences getSharedPreferences(String name,@PreferencesMode int mode);
    public abstract boolean deleteSharedPreferences(String name);
    
    // 数据库相关
    public abstract SQLiteDatabase openOrCreateDatabase(...);
    public abstract boolean deleteDatabase(String name);
    public abstract File getDatabasePath(String name);
    ...
    
    // 其它
    public void registerComponentCallbacks(ComponentCallbacks callback) { ... }
    public void unregisterComponentCallbacks(ComponentCallbacks callback) { ... }
    ...
}

public interface ComponentCallbacks {
    void onConfigurationChanged(Configuration newConfig);
    void onLowMemory();
}

结合注释,可以发现,Context 就相当于 Application 的大管家,主要负责:

  1. 四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等
  2. 获取系统/应用资源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等
  3. 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等
  4. 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等
  5. 其它辅助功能,比如设置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生

ContextImpl 、ContextWrapper、ContextThemeWrapper 有什么区别?

ContextWrapper

先看 ContextWrapper:

/**
* Proxying implementation of Context that simply delegates all of its calls to
* another Context.  Can be subclassed to modify behavior without changing
* the original Context.
*/
public class ContextWrapper extends Context {
    // 注意这个成员
    Context mBase; 

    public ContextWrapper(Context base) {
        mBase = base;
    }
    
    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    
    // 这就是经常让人产生疑惑的  Base Context 了
    public Context getBaseContext() {
        return mBase;
    }

    // 下面这些方法全都直接通过 mBase 完成
    @Override
    public AssetManager getAssets() {
        return mBase.getAssets();
    }

    @Override
    public Resources getResources() {
        return mBase.getResources();
    }

    @Override
    public PackageManager getPackageManager() {
        return mBase.getPackageManager();
    }
    
    ...
    
}

可以看到,ContextWrapper 实际上就是 Context 的代理类而已,所有的操作都是通过内部成员 mBase 完成的,另外,Activity、Service 的 getBaseContext 返回的就是这个 mBase。

ContextThemeWrapper

接着看 ContextThemeWrapper,这个类的代码并不多,主要看 Resource 和 Theme 相关的:

/**
* A context wrapper that allows you to modify or replace the theme of the
* wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
    private int mThemeResource;
    private Resources.Theme mTheme;
    private LayoutInflater mInflater;
    private Configuration mOverrideConfiguration;
    private Resources mResources;

    public ContextThemeWrapper() {
        super(null);
    }

    public ContextThemeWrapper(Context base,@StyleRes int themeResId) {
        super(base);
        mThemeResource = themeResId;
    }

    public ContextThemeWrapper(Context base,Resources.Theme theme) {
        super(base);
        mTheme = theme;
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }

    // 在 Recource 初始化之前,传入配置信息
    public void applyOverrideConfiguration(Configuration overrideConfiguration) {
        if (mResources != null) {
            throw new IllegalStateException(...);
        }
        if (mOverrideConfiguration != null) {
            throw new IllegalStateException(...);
        }
        mOverrideConfiguration = new Configuration(overrideConfiguration);
    }

    public Configuration getOverrideConfiguration() {
        return mOverrideConfiguration;
    }

    // 没有重写 setResource,即 setResource 行为和父类一样
    @Override
    public Resources getResources() {
        return getResourcesInternal();
    }

    private Resources getResourcesInternal() {
        if (mResources == null) {
            if (mOverrideConfiguration == null) {
                mResources = super.getResources();
            } else {
                // 根据配置信息初始化 Resource
                // 注意,这里创建了另一个和 Base Context 不同的 Resource
                final Context resContext = createConfigurationContext(mOverrideConfiguration);
                mResources = resContext.getResources();
            }
        }
        return mResources;
    }

    @Override
    public void setTheme(int resid) {
        if (mThemeResource != resid) {
            mThemeResource = resid;
            initializeTheme();
        }
    }
    
    private void initializeTheme() {
        final boolean first = mTheme == null;
        if (first) {
            // 根据 Resource 获取 Theme
            mTheme = getResources().newTheme();
            // 复制内容
            final Resources.Theme theme = getBaseContext().getTheme();
            if (theme != null) {
                mTheme.setTo(theme);
            }
        }
        onApplyThemeResource(mTheme,mThemeResource,first);
    }

    protected void onApplyThemeResource(Resources.Theme theme,int resId,boolean first) {
        theme.applyStyle(resId,true);
    }

    @Override
    public Resources.Theme getTheme() {
        // 只会初始化一次
        if (mTheme != null) {
            return mTheme;
        }

        mThemeResource = Resources.selectDefaultTheme(mThemeResource,getApplicationInfo().targetSdkVersion);
        initializeTheme();

        return mTheme;
    }

    ...

}

结合注释及源码,可以发现,相比 ContextWrapper,ContextThemeWrapper 有自己的另外 Resource 以及 Theme 成员,并且可以传入配置信息以初始化自己的 Resource 及 Theme。即 Resource 以及 Theme 相关的行为不再是直接调用 mBase 的方法了,也就说,ContextThemeWrapper 和它的 mBase 成员在 Resource 以及 Theme 相关的行为上是不同的。

ContextImpl

下面看一下 ContextImpl 有关 Theme 以及 Resource 的部分,以分析它和 ContextThemeWrapper 的区别:

/**
* Common implementation of Context API,which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {

    private int mThemeResource = 0;
    private Resources.Theme mTheme = null;
    private @NonNull Resources mResources;
    
    // 用于创建 Activity Context
    static ContextImpl createActivityContext(...) {
        ContextImpl context = new ContextImpl(...);
        context.setResources(resourcesManager.createBaseActivityResources(...));
        return context;
    }
    
    // 用于创建 Application Context、Service Context
    static ContextImpl createAppContext(ActivityThread mainThread,LoadedApk packageInfo) {
        ContextImpl context = new ContextImpl(...);
        context.setResources(packageInfo.getResources());
        return context;
    }
    
    private static Resources createResources(...) {
        return ResourcesManager.getInstance().getResources(...);
    }

    // ContextThemeWrapper 没有重写父类的 setResources
    // 因此会调用 mBase 的 setResources,即和 ContextImpl 的行为一样
    void setResources(Resources r) {
        if (r instanceof CompatResources) {
            ((CompatResources) r).setContext(this);
        }
        mResources = r;
    }
    
    @Override
    public Resources getResources() {
        return mResources;
    }
    
    
    /* ---------- 主题相关 ------------ */
    
    @Override
    public void setTheme(int resId) {
        synchronized (mSync) {
            if (mThemeResource != resId) {
                mThemeResource = resId;
                initializeTheme();
            }
        }
    }
    
    // 直接创建一个 Themem 对象,相比 ContextThemeWrapper,少了一部分内容
    private void initializeTheme() {
        if (mTheme == null) {
            mTheme = mResources.newTheme();
        }
        mTheme.applyStyle(mThemeResource,true);
    }

    @Override
    public Resources.Theme getTheme() {
        synchronized (mSync) {
            // 和 ContextThemeWrapper 基本一样
            if (mTheme != null) {
                return mTheme;
            }

            mThemeResource = Resources.selectDefaultTheme(mThemeResource,getOuterContext().getApplicationInfo().targetSdkVersion);
            initializeTheme();

            return mTheme;
        }
    }

}

从代码中可以看出,ContextImpl 和 ContextThemeWrapper 最大的区别就是没有一个 Configuration 而已,其它的行为大致是一样的。另外,ContextImpl 可以用于创建 Activity、Service 以及 Application 的 mBase 成员,这个 Base Context 时除了参数不同,它们的 Resource 也不同。需要注意的是,createActivityContext 等方法中 setResource 是 mBase 自己调用的,Activity、Service 以及 Application 本身并没有执行 setResource。

小结

  1. ContextWrapper、ContextThemeWrapper 都是 Context 的代理类,二者的区别在于 ContextThemeWrapper 有自己的 Theme 以及 Resource,并且 Resource 可以传入自己的配置初始化

  2. ContextImpl 是 Context 的主要实现类,Activity、Service 和 Application 的 Base Context 都是由它创建的,即 ContextWrapper 代理的就是 ContextImpl 对象本身

  3. ContextImpl 和 ContextThemeWrapper 的主要区别是, ContextThemeWrapper 有 Configuration 对象,Resource 可以根据这个对象来初始化

  4. Service 和 Application 使用同一个 Recource,和 Activity 使用的 Resource 不同

Activity Context、Service Context、Application Context、Base Context 有什么区别?

Activity Context

先看 Activity,Activity 在启动时,最终会执行 ActivityThread 的 performLaunchActivitiy:

public final class ActivityThread {

    private Activity performLaunchActivity(ActivityClientRecord r,Intent customIntent) {
        ...
        // 这个 Context 将会作为 Activity 的 Base Context
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            ClassLoader cl = appContext.getClassLoader();
            // 创建 Activity
            activity = mInstrumentation.newActivity(
                    cl,component.getClassName(),r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
        } catch (Exception e) {
            ...
        }

        try {
            // 创建 Application
            Application app = r.packageInfo.makeApplication(false,mInstrumentation);

            if (activity != null) {
                // 初始化 Activity,注意参数 appContext
                activity.attach(appContext,...);
                ...
            }

        } catch (...) {
            ...
        }

        return activity;
    }
    
    private ContextImpl createBaseContextForActivity(ActivityClientRecord r) {
        ContextImpl appContext = ContextImpl.createActivityContext(...);
        ...
    }
    
}

可以看到,Activity 的 Base Context 就是上面分析过的 ContextImpl 的 createActivityContext 创建的。

同时,Service 的 Base Context 的创建过程和 Application 一样,调用的都是 ContextImpl 的 createAppContext,即 Service Context 和 Application Context 的 Resource 是相同的。因此这里跳过 Service,下面看一下 Application Context。

Application Context

在上面 ActivityThread 的 performLaunchActivity 方法中,可以看到一个 makeApplication 的调用,它是 LoaedApk 的方法:

public final class LoadedApk {

    private Application mApplication;

    public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }

        Application app = null;

        try {
            // 创建 Base Context
            ContextImpl appContext = ContextImpl.createAppContext(mActivityThread,this);
            // 创建 Application 并设置 Base Context
            app = mActivityThread.mInstrumentation.newApplication(
                    cl,appClass,appContext);
            appContext.setOuterContext(app);
        } catch (Exception e) {
            ...
        }
        // Application 创建成功,赋值给 mApplication
        mApplication = app;
        
        ...
        
        return app;
    }
    
    // 获取 mApplication
    Application getApplication() {
        return mApplication;
    }
    
}
public class Application extends ContextWrapper implements ComponentCallbacks2 {
    /* package */ final void attach(Context context) {
        // 调用父类的 attachBaseContext 以设置 mBase
        attachBaseContext(context); 
    }
}

可以看到,Instrumentation 是使用反射的方法创建 Application 对象,创建完毕后,会执行 Application 的 attach 方法设置 mBase 成员。

Application 及其 Base Context 的创建过程我们了解了,接下来看一下 getApplicationContext 的实现:

class ContextImpl extends Context {

    ActivityThread mMainThread;
    LoadedApk mPackageInfo;
    
    @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    
}

从代码中可以看出,getApplicationContext 的返回值可能有两个:第一个是 LoadedApk 的 getApplication 方法,这个方法的返回值就是刚刚创建的 Application 对象;第二个是 ActivityThread 的 getApplication 方法:

public final class ActivityThread {

    Application mInitialApplication;
    
    public Application getApplication() {
        return mInitialApplication;
    }
    
    public static ActivityThread systemMain() {
        // 创建 ActivityThread
        ActivityThread thread = new ActivityThread();
        thread.attach(true);
        return thread;
    }
    
    private void attach(boolean system) {
        mSystemThread = system;
        if (!system) {
            ...
        } else {
            try {
                mInstrumentation = new Instrumentation();
                // 注意参数 getSystemContext().mPackageInfo
                ContextImpl context = ContextImpl.createAppContext(
                        this,getSystemContext().mPackageInfo);
                // 创建 Application
                mInitialApplication = context.mPackageInfo.makeApplication(true,null);
                mInitialApplication.onCreate();
            } catch (Exception e) {
                ...
            }
        }
        ...
    }
    
}

ActivityThread 中的 mInitialApplication 是在 systemMain 方法执行时创建的,而这个方法又是 SystemServer 启动时调用的,结合参数 getSystemContext().mPackageInfo,因此个人推测 mInitialApplication 对应的是系统的某个 apk,即系统级别的 Application,但具体是不是这样,目前还没有深入研究过,有兴趣的可以自己研究。

为什么不推荐使用 Base Context?

一般情况下,使用代理而不直接使用某个对象,目的可能有两个:

  1. 定制自己的行为
  2. 不影响原对象

其中 Servcie 和 Application 的父类 ContextWrapper 完全没有自定义的行为,而 Activity 的父类 ContextThemeWrapper 则自定义了 Resource 以及 Theme 的相关行为,因此,个人理解:

  1. 对于 Service 和 Application 而言,不推荐使用 Base Context,是担心用户修改了 Base Context 而导致错误的发生
  2. 对于 Activity 而言,除了担心用户的修改之外,Base Context 和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话),使用 Base Context 可能会出现无法预期的现象

对于 Activity 的 getResource 问题,我写了一份代码来验证:

public class MainActivity extends AppCompatActivity {

    private Configuration mOverrideConfiguration;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG,"getResources: " + getResources() + ",getBaseContext().getResources():"
                + getBaseContext().getResources());
    }
    
    // 因为 Android 会在 onCreate 之前自动调用 getResource
    // 因此需要在这里执行 applyOverrideConfiguration
    @Override
    public Resources getResources() {
        if (mOverrideConfiguration == null) {
            mOverrideConfiguration = new Configuration();
            applyOverrideConfiguration(mOverrideConfiguration);
        }
        return super.getResources();
    }
    
}

输出(我用的是小米手机):

getResources: android.content.res.MiuiResources@3c660a7,getBaseContext().getResources():android.content.res.MiuiResources@5143954

可以看到,就像源码显示的那样,应用了 Configuration 之后,Activity 的 getResource 方法返回的和 getBaseContext().getResources() 方法返回的不是同一个对象

小结

  1. Activity、Service 和 Application 的 Base Context 都是由 ContextImpl 创建的,且创建的都是 ContextImpl 对象,即它们都是 ContextImpl 的代理类
  2. getApplicationContext 返回的就是 Application 对象本身,一般情况下它对应的是应用本身的 Application 对象,但也可能是系统的某个 Application
  3. 对于 Service 和 Application 而言,不推荐使用 Base Context,是担心用户修改了 Base Context 而导致错误的发生
  4. 对于 Activity 而言,除了担心用户的修改之外,Base Context 和 Activity 本身对于 Reource 以及 Theme 的相关行为是不同的(如果应用了 Configuration 的话),使用 Base Context 可能会出现无法预期的现象

总结

Context 的继承关系如下:

image

Context 相当于 Application 的大管家,主要负责:

  1. 四大组件的交互,包括启动 Activity、Broadcast、Service,获取 ContentResolver 等
  2. 获取系统/应用资源,包括 AssetManager、PackageManager、Resources、System Service 以及 color、string、drawable 等
  3. 文件,包括获取缓存文件夹、删除文件、SharedPreference 相关等
  4. 数据库(SQLite)相关,包括打开数据库、删除数据库、获取数据库路径等
  5. 其它辅助功能,比如设置 ComponentCallbacks,即监听配置信息改变、内存不足等事件的发生

ContextWrapper、ContextThemeWrapper、ContextImpl 的区别:

  1. ContextWrapper、ContextThemeWrapper 都是 Context 的代理类,二者的区别在于 ContextThemeWrapper 有自己的 Theme 以及 Resource,并且 Resource 可以传入自己的配置初始化
  2. ContextImpl 是 Context 的主要实现类,Activity、Service 和 Application 的 Base Context 都是由它创建的,即 ContextWrapper 代理的就是 ContextImpl 对象本身
  3. ContextImpl 和 ContextThemeWrapper 的主要区别是, ContextThemeWrapper 有 Configuration 对象,Resource 可以根据这个对象来初始化

Activity Context、Service Context、Application Context、Base Context 的区别:

  1. Activity、Service 和 Application 的 Base Context 都是由 ContextImpl 创建的,且创建的都是 ContextImpl 对象,即它们都是 ContextImpl 的代理类
  2. Service 和 Application 使用同一个 Recource,和 Activity 使用的 Resource 不同
  3. getApplicationContext 返回的就是 Application 对象本身,一般情况下它对应的是应用本身的 Application 对象,但也可能是系统的某个 Application

原文地址:https://www.cnblogs.com/linhaostudy

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


AdvserView.java package com.earen.viewflipper; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory;
ImageView的scaleType的属性有好几种,分别是matrix(默认)、center、centerCrop、centerInside、fitCenter、fitEnd、fitStart、fitXY。 |值|说明| |:--:|:--| |center|保持原图的大小,显示在ImageVie
文章浏览阅读8.8k次,点赞9次,收藏20次。本文操作环境:win10/Android studio 3.21.环境配置 在SDK Tools里选择 CMAKE/LLDB/NDK点击OK 安装这些插件. 2.创建CMakeLists.txt文件 在Project 目录下,右键app,点击新建File文件,命名为CMakeLists.txt点击OK,创建完毕! 3.配置文件 在CMa..._link c++ project with gradle
文章浏览阅读1.2w次,点赞15次,收藏69次。实现目的:由mainActivity界面跳转到otherActivity界面1.写好两个layout文件,activity_main.xml和otherxml.xmlactivity_main.xml&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;RelativeLayout ="http://schemas..._android studio 界面跳转
文章浏览阅读3.8w次。前言:最近在找Android上的全局代理软件来用,然后发现了这两款神作,都是外国的软件,而且都是开源的软件,因此把源码下载了下来,给有需要研究代理这方面的童鞋看看。不得不说,国外的开源精神十分浓,大家相互使用当前基础的开源软件,然后组合成一个更大更强的大开源软件。好吧,废话不多说,下面简单介绍一下这两款开源项目。一、ProxyDroid:ProxyDroid功能比较强大,用到的技术也比较多,源码也_proxydroid
文章浏览阅读2.5w次,点赞17次,收藏6次。创建项目后,运行项目时Gradle Build 窗口却显示错误:程序包R不存在通常情况下是不会出现这个错误的。我是怎么遇到这个错误的呢?第一次创建项目,company Domain我使用的是:aven.com,但是创建过程在卡在了Building 'Calculator' Gradle Project info这个过程中,于是我选择了“Cancel”第二次创建项目,我还是使用相同的项目名称和项目路_r不存在
文章浏览阅读8.9w次,点赞4次,收藏43次。前言:在Android上使用系统自带的代理,限制灰常大,仅支持系统自带的浏览器。这样像QQ、飞信、微博等这些单独的App都不能使用系统的代理。如何让所有软件都能正常代理呢?ProxyDroid这个软件能帮你解决!使用方法及步骤如下:一、推荐从Google Play下载ProxyDroid,目前最新版本是v2.6.6。二、对ProxyDroid进行配置(基本配置:) (1) Auto S_proxydroid使用教程
文章浏览阅读1.1w次,点赞4次,收藏17次。Android Studio提供了一个很实用的工具Android设备监视器(Android device monitor),该监视器中最常用的一个工具就是DDMS(Dalvik Debug Monitor Service),是 Android 开发环境中的Dalvik虚拟机调试监控服务。可以进行的操作有:为测试设备截屏,查看特定进程中正在运行的线程以及堆栈信息、Logcat、广播状态信息、模拟电话_安卓摄像头调试工具
文章浏览阅读2.1k次。初学Android游戏开发的朋友,往往会显得有些无所适从,他们常常不知道该从何处入手,每当遇到自己无法解决的难题时,又往往会一边羡慕于 iPhone下有诸如Cocos2d-iphone之类的免费游戏引擎可供使用,一边自暴自弃的抱怨Android平台游戏开发难度太高,又连个像样的游 戏引擎也没有,甚至误以为使用Java语言开发游戏是一件费力不讨好且没有出路的事情。事实上,这种想法完全是没有必_有素材的游戏引擎
文章浏览阅读3.2k次,点赞2次,收藏2次。2014年12月从csdn专家福利获得的一本书《Android游戏开发技术实战详解》,尘封了一年多的时间,今天才翻开来看。我认识中的Android,提到Android最先浮现在我脑海中的是那可爱的机器人图标:这个Logo是由Ascender公司设计的,诞生于2010年,其设计灵感源于男女厕所门上的图形符号(真的是灵感无处不在),于是布洛克绘制了一个简单的机器人,它的躯干就像锡罐的形状,头上还有两根_智能手机的特点有哪些?
文章浏览阅读8.1k次,点赞9次,收藏11次。首先,Android是不是真的找工作越来越难呢?这个可能是大家最关心的。这个受大的经济环境以及行业发展前景的影响,同时也和个人因素有关。2016-08-26近期一方面是所在的公司招聘Java开发人员很难招到合适的,投简历的人很少;而另一方面,经常听身边的人说Android、iOS方面找工作不好找,特别是没什么经验的,经验比较少的!说是不好找,但在我家所在的吉林省省会长春,会Unity3D+Maya_android 开发和asp.net哪个好 site:blog.csdn.net
文章浏览阅读6.1k次。在上篇“走进Android开发的世界,HelloWorld”,我们创建了一个Android 项目 HelloWorld,并演示了如何通过USB连接手机查看运行效果;而如果没有手机或没有对应型号的手机,又想做对应型号(屏幕尺寸、Android系统版本)的适配,应该怎么办呢?这时Android模拟器就派上用场了。Android模拟器Android SDK自带一个移动模拟器。它是一个可以运行在你电脑上的_安卓移动开发软件怎样预览
文章浏览阅读8.9k次。Google IO 2017 上宣布,将Kotlin语言作为安卓开发的官方语言。Kotlin由JetBrains公司开发,与Java 100%互通,并具备诸多Java尚不支持的新特性。谷歌称还将与JetBrains公司合作,为Kotlin设立一个非盈利基金会。Kotlin 是一个基于 JVM 的静态类型编程语言,Kotlin可以编译成Java字节码,也可以编译成JavaScript,方便在没有JV_kotlin为什么被嫌弃
文章浏览阅读9.6w次,点赞17次,收藏35次。有些情况下,不方便使用断点的方式来调试,而是希望在控制台打印输出日志,使用过Eclipse的同学都知道Java可以使用 System.out.println(""); 来在控制台打印输出日志,但是在android studio中却是不行的,还是有差别的,那应该用什么呢?android.util.Log在调试代码的时候我们需要查看调试信息,那我们就需要用Android Log类。android.ut_andirod.studio 为什么不在控制台打印输出
文章浏览阅读8.2k次,点赞2次,收藏8次。在上篇“走进Android开发的世界,HelloWorld”,我们创建了一个Android 项目 HelloWorld,并演示了如何通过USB连接手机查看运行效果;这里讲一下如何为应用添加一个按钮,并为按钮添加Click单击事件处理程序,显示/隐藏另一个按钮。添加按钮在HelloWorld项目的基础上,打开界面布局文件:activity_main.xml切换到Design(设计)模式;在组件But_activity_main.xml按钮隐藏
文章浏览阅读2.9k次,点赞3次,收藏9次。android 开发工具主流的还是Android Studio,当然也有很多人喜欢用Eclipse,也有人喜欢用IntelliJ IDEA ;还有Xamarin这种只需要编写一次代码,可以编译多种平台可运行的强大工具。但是它又真的强大吗?就我看来没有,身边很多人还是在用Android Studio、XCode开发应用,没见谁在用Xamarin之类的工具。系统要求WindowsMicrosoft®_android开发下载安装
文章浏览阅读4.2k次,点赞7次,收藏26次。你知道Hello World程序的由来吗?对于大多数编程语言的学习来说,真正入门的一课就是 Hello World!会而不难,难而不会。虽然很多人写过关于Android开发Hello World的文章,但随着时间的推移,开发工具、技术的进步,可能有些已经过时了。我就记录一下当下我所经历的第一个Android APP HelloWorld。一、准备1、开发环境参考:Android Studio 下载_android helloworld textview 句柄获取
这篇“android轻量级无侵入式管理数据库自动升级组件怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定...
今天小编给大家分享一下Android实现自定义圆形进度条的常用方法有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文...
这篇文章主要讲解了“Android如何解决字符对齐问题”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Android...