Recommended: "2020 Android interview questions summary [Collection]"
In Android development, whether it is a plug-in Whether it is componentization or componentization, they are all designed based on the ClassLoader of the Android system. It’s just that the virtual machine on the Android platform runs Dex bytecode, a product of class file optimization. A traditional Class file is a Java source code file that generates a .class file, while Android merges and optimizes all Class files. , and then generate a final class.dex. The purpose is to keep only one copy of duplicate things in different class files. In the early Android application development, if the Android application was not divided into dex, then the apk of the last application would only be There will be a dex file.
There are two commonly used class loaders in Android, DexClassLoader and PathClassLoader, both of which inherit from BaseDexClassLoader. The difference is that when calling the parent class constructor, DexClassLoader passes an additional optimizedDirectory parameter. This directory must be an internal storage path and is used to cache Dex files created by the system. The parameter of PathClassLoader is null and can only load Dex files in the internal storage directory. So we can use DexClassLoader to load external apk files, which is also the basis of many plug-in technologies.
Understanding Android Service can be understood from the following aspects:
IntentService is an abstract class, inherited from Service, and there is a ServiceHandler (Handler) and HandlerThread (Thread) internally. IntentService is a class that handles asynchronous requests. There is a worker thread (HandlerThread) in IntentService to handle time-consuming operations. The method of starting IntentService is the same as normal, but when the task is completed, IntentService will automatically stop. In addition, IntentService can be started multiple times. Each time-consuming operation will be executed in the onHandleIntent callback of IntentService in the form of a work queue, and one worker thread will be executed each time. The essence of IntentService is: an asynchronous framework that encapsulates a HandlerThread and Handler.
Service is one of the four major components of Android and is widely used. Like Activity, Service also has a series of life cycle callback functions, as shown in the figure below.
Generally, there are two ways to start Service, startService and bindService.
When we call the startService method of Context, we start the Service. The Service started by the startService method will continue to run indefinitely. The Service will stop running and be destroyed only when the stopService of Context is called externally or the stopSelf method of Service is called internally.
onCreate: When executing the startService method, if the Service is not running, the Service will be created and the onCreate callback method of the Service will be executed; if the Service is already running, then the startService method will not be executed. The onCreate method of Service will be executed. That is to say, if the startService method of Context is executed multiple times to start the Service, the onCreate method of the Service method will only be called once when the Service is created for the first time, and will not be called again in the future. We can complete some Service initialization related operations in the onCreate method.
onStartCommand: After executing the startService method, the onCreate method of the Service may be called, after which the onStartCommand callback method of the Service will definitely be executed. In other words, if the startService method of Context is executed multiple times, the onStartCommand method of Service will also be called multiple times accordingly. The onStartCommand method is very important. In this method, we perform actual operations based on the incoming Intent parameters. For example, a thread will be created here to download data or play music, etc.
public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) { }
When Android faces a shortage of memory, it may destroy your currently running Service, and then re-create the Service when the memory is sufficient. The behavior of the Service being forcibly destroyed and re-created by the Android system depends on the onStartCommand method in the Service. The return value. There are three commonly used return values that we use, START_NOT_STICKY
, START_STICKY
and START_REDELIVER_INTENT
. These three values are static constants in Service.
START_NOT_STICKY
If START_NOT_STICKY is returned, it means that when the process running the Service is forcibly killed by the Android system, the Service will not be re-created. Of course, if it is killed If startService is called again after a period of time, the Service will be instantiated again. So under what circumstances is it appropriate to return this value? If it doesn't matter how many times the work performed by one of our Services is interrupted or it needs to be killed and not re-created immediately when Android memory is tight, this behavior is acceptable, then we can set the return value of onStartCommand to START_NOT_STICKY. For example, a service needs to regularly obtain the latest data from the server: use a timer to start the service every specified N minutes to obtain the latest data from the server. When the onStartCommand of the Service is executed, a timer is planned in this method for N minutes to start the Service again and open a new thread to perform network operations. Assuming that the Service is forcibly killed by the Android system during the process of obtaining the latest data from the server, the Service will not be re-created. This does not matter, because the timer will start the Service again and re-obtain the data after N minutes.
START_STICKY
If START_STICKY is returned, it means that after the process running the Service is forcibly killed by the Android system, the Android system will still set the Service to the started state (that is, running status), but the intent object passed in the onStartCommand method is no longer saved, and then the Android system will try to recreate the Service again and execute the onStartCommand callback method, but the Intent parameter of the onStartCommand callback method is null, that is, although the onStartCommand method will be executed, Unable to obtain intent information. If your Service can run or end at any time without any problem, and does not require intent information, then you can return START_STICKY in the onStartCommand method. For example, a Service used to play background music is suitable for returning this value.
START_REDELIVER_INTENT
If START_REDELIVER_INTENT is returned, it means that after the process running the Service is forcibly killed by the Android system, similar to the case of returning START_STICKY, the Android system will recreate it again The Service and execute the onStartCommand callback method, but the difference is that the Android system will retain the Intent that was last passed into the onStartCommand method before the Service was killed and pass it again to the onStartCommand method of the recreated Service. , so that we can read the intent parameters. As long as START_REDELIVER_INTENT is returned, the intent in onStartCommand must not be null. If our Service needs to rely on a specific Intent to run (needs to read relevant data information from the Intent, etc.), and it is necessary to recreate it after forced destruction, then such a Service is suitable for returning START_REDELIVER_INTENT.
The onBind method in Service is an abstract method, so the Service class itself is an abstract class, that is, the onBind method must be rewritten, even if we don't use it. When using Service through startService, we only need to return null when overriding the onBind method. The onBind method is mainly used when calling Service for the bindService method.
onDestroy: The Service started through the startService method will run indefinitely. Only when the StopService of the Context is called or the stopSelf method is called inside the Service, the Service will stop running and be destroyed. The Service callback function will be executed when destroyed.
The bindService method starts the Service mainly with the following life cycle functions:
The system will call this method when the service is first created. If the service is already running, this method is not called and is called only once.
The system will call this method when another component requests to start the service by calling startService().
The system will call this method when the service is no longer in use and will be destroyed.
The system will call this method when another component binds to the service by calling bindService().
The system will call this method when another component unbinds from the service by calling unbindService().
When the old component is unbound from the service, another new component is bound to the service, and onUnbind() returns true, the system will call this method.
首先我们需要创建一个xml文件,然后创建与之对应的java文件,通过onCreatView()的返回方法进行关联,最后我们需要在Activity中进行配置相关参数即在Activity的xml文件中放上fragment的位置。
<fragment android:name="xxx.BlankFragment" android:layout_width="match_parent" android:layout_height="match_parent"> </fragment>
动态创建Fragment主要有以下几个步骤:
FragmnetPageAdapter在每次切换页面时,只是将Fragment进行分离,适合页面较少的Fragment使用以保存一些内存,对系统内存不会多大影响。
FragmentPageStateAdapter在每次切换页面的时候,是将Fragment进行回收,适合页面较多的Fragment使用,这样就不会消耗更多的内存
Activity的生命周期如下图:
动态加载时,Activity的onCreate()调用完,才开始加载fragment并调用其生命周期方法,所以在第一个生命周期方法onAttach()中便能获取Activity以及Activity的布局的组件;
1.静态加载时,Activity的onCreate()调用过程中,fragment也在加载,所以fragment无法获取到Activity的布局中的组件,但为什么能获取到Activity呢?
2.原来在fragment调用onAttach()之前其实还调用了一个方法onInflate(),该方法被调用时fragment已经是和Activity相互结合了,所以可以获取到对方,但是Activity的onCreate()调用还未完成,故无法获取Activity的组件;
3.Activity的onCreate()调用完成是,fragment会调用onActivityCreated()生命周期方法,因此在这儿开始便能获取到Activity的布局的组件;
fragment不通过构造函数进行传值的原因是因为横屏切换的时候获取不到值。
Activity向Fragment传值,要传的值放到bundle对象里;
在Activity中创建该Fragment的对象fragment,通过调用setArguments()传递到fragment中;
在该Fragment中通过调用getArguments()得到bundle对象,就能得到里面的值。
在Activity中调用getFragmentManager()得到fragmentManager,,调用findFragmentByTag(tag)或者通过findFragmentById(id),例如:
FragmentManager fragmentManager = getFragmentManager(); Fragment fragment = fragmentManager.findFragmentByTag(tag);
通过回调的方式,定义一个接口(可以在Fragment类中定义),接口中有一个空的方法,在fragment中需要的时候调用接口的方法,值可以作为参数放在这个方法中,然后让Activity实现这个接口,必然会重写这个方法,这样值就传到了Activity中
通过findFragmentByTag得到另一个的Fragment的对象,这样就可以调用另一个的方法了。
通过接口回调的方式。
通过setArguments,getArguments的方式。
一种是add方式来进行show和add,这种方式你切换fragment不会让fragment重新刷新,只会调用onHiddenChanged(boolean isHidden)。
而用replace方式会使fragment重新刷新,因为add方式是将fragment隐藏了而不是销毁再创建,replace方式每次都是重新创建。
两者都可以提交fragment的操作,唯一的不同是第二种方法,允许丢失一些界面的状态和信息,几乎所有的开发者都遇到过这样的错误:无法在activity调用了onSaveInstanceState之后再执行commit(),这种异常时可以理解的,界面被系统回收(界面已经不存在),为了在下次打开的时候恢复原来的样子,系统为我们保存界面的所有状态,这个时候我们再去修改界面理论上肯定是不允许的,所以为了避免这种异常,要使用第二种方法。
When we often use fragment, we often use it in conjunction with viewpager. Then we will encounter a problem, that is, when initializing the fragment, it will be included with the network request we wrote. Execution is very performance consuming. The ideal way is to only request the network when the user clicks or slides to the current fragment. Therefore, we came up with the term lazy loading.
Viewpager is used with fragments, and the first two fragments are loaded by default. It is easy to cause problems such as network packet loss and congestion.
There is a setUserVisibleHint method in Fragment, and this method is better than the onCreate() method. It will tell us whether the current Fragment is visible through isVisibleToUser, and we can load the network again when it is visible.
Looking from the log, setUserVisibleHint() is called earlier than onCreateView, so if you want to implement lazy loading in setUserVisibleHint(), you must ensure that View and other variables have been initialized to avoid null pointers.
Usage steps:
Declare a variable isPrepare=false, isVisible=false, indicating whether the current page has been created
Set isPrepare=true during the onViewCreated cycle
In setUserVisibleHint ( boolean isVisible) to determine whether to display, set isVisible=true
Judge isPrepare and isVisible, both are true to start loading data, and then restore isPrepare and isVisible to false to prevent repeated loading.
Regarding the lazy loading of Android Fragment, you can refer to the following link: Lazy loading of Fragment
User Click the application icon from the Launcher program to start the application's entry Activity. When the Activity starts, it requires interaction between multiple processes. There is a zygote process in the Android system dedicated to incubating the processes of the Android framework layer and application layer programs. There is also a system_server process, which runs many binder services. For example, ActivityManagerService, PackageManagerService, WindowManagerService, these binder services run in different threads respectively, among which ActivityManagerService is responsible for managing the Activity stack, application process, and task.
When the user clicks the application icon in the Launcher program, the ActivityManagerService will be notified to start the application's entry Activity. If the ActivityManagerService finds that the application has not been started, it will notify the Zygote process. Hatch the application process, and then execute the main method of ActivityThread in this dalvik application process. The application process next notifies ActivityManagerService that the application process has been started. ActivityManagerService saves a proxy object of the application process so that ActivityManagerService can control the application process through this proxy object. Then ActivityManagerService notifies the application process to create an instance of the entry Activity and execute its life cycle method.
Android drawing process window startup process analysis
4.2, Activity life cycle
Activity is in the active state. At this time, the Activity is on the top of the stack, is visible, and can interact with the user.
When the Activity loses focus, or is placed on the top of the stack by a new non-full-screen Activity, or by a transparent Activity, the Activity is converted to the Paused state. But we need to understand that at this time the Activity has only lost the ability to interact with the user, and all its status information and member variables still exist. Only when the system memory is tight can it be recycled by the system.
When an Activity is completely covered by another Activity, the covered Activity will enter the Stopped state. At this time, it is no longer visible, but maintains its status as the Paused state. All status information and its member variables.
When the Activity is recycled by the system, the Activity is in the Killed state.
Activity will switch between the above four forms. As for how to switch, it depends on the user's operation. After understanding the four forms of Activity, let's talk about the life cycle of Activity.
The so-called typical life cycle is that with user participation, Activity goes through the normal life cycle process from creation, running, stopping, and destruction.
This method is called back when the Activity is created. It is the first method called in the life cycle. We generally need to override this method when creating the Activity, and then in the Do some initialization operations in the method, such as setting interface layout resources through setContentView, initializing required component information, etc.
When this method is called back, it means that the Activity is starting. At this time, the Activity is already visible, but it has not yet been displayed in the foreground, so it cannot interact with the user. It can be simply understood that the Activity has been displayed and we cannot see it.
Когда этот метод вызывает обратный вызов, это означает, что действие отображается на переднем плане и может взаимодействовать с пользователем (в активном/выполненном состоянии, упомянутом ранее).Метод onResume имеет два сходства с onStart.Все говорили, что Activity видна, но при обратном вызове onStart Activity все еще находится в фоновом режиме и не может взаимодействовать с пользователем, а onResume уже отображается на переднем плане и может взаимодействовать с пользователем. Конечно, из блок-схемы мы также можем видеть, что когда Activity останавливается (вызываются методы onPause и onStop), метод onResume также будет вызываться при возвращении на передний план, поэтому мы также можем инициализировать некоторые ресурсы в onResume. метод, такой как повторная инициализация ресурсов, выпущенных в методе onPause или onStop.
Когда этот метод вызывается обратно, это означает, что активность останавливается (состояние паузы).При нормальных обстоятельствах метод onStop будет вызван немедленно. Но на блок-схеме мы также можем увидеть ситуацию, когда метод onResume выполняется непосредственно после выполнения метода onPause. Это относительно экстремальное явление. Это может быть связано с операцией пользователя, которая привела к возврату текущего действия в исходное состояние. фон, а затем быстро вернуться к нему. Для текущего действия в это время будет вызван метод onResume. Конечно, в методе onPause мы можем выполнять некоторые операции по хранению данных, остановке анимации или переработке ресурсов, но это не должно занимать слишком много времени, поскольку это может повлиять на отображение нового Activity — после выполнения метода onPause onResume нового метода Activity будет выполнен.
Как правило, он выполняется сразу после завершения метода onPause, что указывает на то, что действие вот-вот остановится или полностью закрыто (состояние «Остановлено»). невидим и работает только в фоновом режиме. Аналогичным образом некоторые операции по освобождению ресурсов можно выполнить в методе onStop (не слишком трудоемко).
Указывает, что действие перезапускается. Когда действие меняется с невидимого на видимое, этот метод вызывается обратно. Обычно эта ситуация возникает, когда пользователь открывает новое действие, текущее действие будет приостановлено (выполняются onPause и onStop), а затем, когда пользователь возвращается на текущую страницу действия, метод onRestart будет вызван обратно.
В настоящее время действие уничтожается, и это также последний метод, который выполняется в жизненном цикле. Как правило, мы можем выполнить некоторую работу по переработке и окончательному выпуску ресурсов. в этом методе.
Здесь мы подходим к подведению итогов. Когда запускается Activity, onCreate(), onStart(), onResume() будут вызываться последовательно, а когда Activity отступает на задний план (невидимый , нажмите «Домой» или будете полностью покрыты новым действием), onPause() и onStop() будут вызываться последовательно. Когда действие возвращается на передний план (возвращается к исходному действию с рабочего стола или возвращается к исходному действию после перезаписи), onRestart(), onStart() и onResume() будут вызываться последовательно. Когда действие завершается и уничтожается (нажмите кнопку «Назад»), onPause(), onStop() и onDestroy() будут вызываться последовательно. На этом этапе весь обратный вызов метода жизненного цикла действия завершается. Теперь давайте вернемся к предыдущей блок-схеме, она должна быть совершенно ясной. Ну, это весь типичный процесс жизненного цикла Activity.
Взаимосвязь между Android Activity, PhoneWindow и DecorView можно представить следующей схемой:
Например, существует следующее представление: DecorView — это представление верхнего уровня всего интерфейса окна. Оно имеет только один дочерний элемент — LinearLayout. Представляет весь интерфейс окна, включая панель уведомлений, строку заголовка и панель отображения содержимого. В LinearLayout есть два дочерних элемента FrameLayout.
DecorView — это представление верхнего уровня, которое по сути представляет собой FrameLayout. Оно состоит из двух частей: строки заголовка и панели содержимого. из них FrameLayout. Идентификатор столбца контента — это контент, который является частью действия, в котором установлен setContentView. Наконец, макет добавляется в FrameLayout с идентификатором контента.
Получить контент: ViewGroup content=findViewById(android.id.content)
Получить набор View: getChildAt(0).
Каждое действие содержит объект Window , объект Window обычно реализуется PhoneWindow.
PhoneWindow: установите DecorView в качестве корневого представления всего окна приложения, которое является классом реализации Window. Это самая простая оконная система в Android. Каждое действие создает объект PhoneWindow, который является интерфейсом для взаимодействия между действием и всей системой представления.
DecorView: это представление верхнего уровня, которое представляет конкретный контент, отображаемый в PhoneWindow. DecorView является предком всех представлений в текущем действии. Он ничего не представляет пользователю.
Механизм распределения событий представления можно представить следующим рисунком:
Как показано на рисунке выше, рисунок разделен на 3 слоя, сверху вниз. Далее идут Activity, ViewGroup и View.
Когда генерируется событие щелчка, процесс его доставки будет следовать следующей последовательности:
Activity -> Окно -> Просмотр
Событие всегда передается в действие, затем действие передается в окно, и, наконец, окно передается в представление верхнего уровня. После получения события , представление верхнего уровня будет распространять событие в соответствии с механизмом распределения событий. Если onTouchEvent представления возвращает FALSE , то будет вызван onTouchEvent его родительского контейнера и т. д. Если ни один из них не обработает это событие, то Activity будет вызвано. обрабатывать это событие.
Для процесса распространения событий ViewGroup это, вероятно, так: если событие перехвата ViewGroup верхнего уровня, то есть onInterceptTouchEvent, возвращает true, событие будет передано ViewGroup для обработки. Если установлен onTouchListener ViewGroup, то будет вызван onTouch, в противном случае будет вызван onTouchEvent, то есть: оба Если установлены оба, onTouch будет блокировать onTouchEvent. В onTouchEvent, если установлен onClickerListener, то onClick будет Если ViewGroup верхнего уровня не перехватывает его, событие будет передано дочернему представлению события щелчка, в котором оно находится. В это время диспетчерское событие подпредставления будет называться
dispatchTouchEvent -> onTouch(setOnTouchListener) -> onTouchEvent -> onClick
onTouch Разница между onTouchEvent
Оба вызываются в sendTouchEvent. onTouch имеет приоритет над onTouchEvent Если onTouch возвращает true, то onTouchEvent не будет выполнен, и onClick не будет выполнен.
В xml-файле макета нашим параметрам Layout_width и Layout_height не нужно писать конкретные размеры, а нужно прописывать wrap_content или match_parent. Эти две настройки не определяют фактический размер, но вид, который мы рисуем на экране, должен иметь определенную ширину и высоту.Именно по этой причине мы должны сами обрабатывать и устанавливать размер. Конечно, класс View обеспечивает обработку по умолчанию, но если обработка по умолчанию класса View не соответствует нашим требованиям, нам придется переписать функцию onMeasure~.
Функция onMeasure представляет собой целое число типа int, которое содержит режим измерения и размер. Данные типа int занимают 32 бита, и Google реализует то, что первые 2 бита данных int используются для различения различных режимов макета, а следующие 30 битов хранят данные о размере.
Функция onMeasure используется, как показано ниже:
MeasureSpec имеет три режима измерения:
match_parent—>ТОЧНО. Как понять это? match_parent заключается в использовании всего оставшегося пространства, предоставленного родительским представлением, и определяется оставшееся пространство родительского представления, которое представляет собой размер, хранящийся в целом числе этого режима измерения.
wrap_content—>AT_MOST. Как понять: мы хотим установить размер для обертывания содержимого нашего представления, тогда размер — это размер, предоставленный нам родительским представлением в качестве ссылки. Пока он не превышает этот размер, конкретный размер будет установлен в соответствии с нашим потребностям.
Фиксированный размер (например, 100 dp) —>ТОЧНО. Если пользователь укажет размер, нам больше не нужно вмешиваться, конечно, указанный размер будет основным.
Настройка ViewGroup не так проста~, потому что она должна не только заботиться о себе , и его подвиды также должны быть приняты во внимание. Мы все знаем, что ViewGroup — это контейнер представлений, который содержит дочерний вид и отвечает за размещение дочернего представления в указанном месте.
Прежде всего, мы должны знать размер каждого подпредставления.Только зная размер подпредставления, мы можем узнать, насколько большой должна быть текущая группа представлений, чтобы разместить их.
Определите размер ViewGroup на основе размера подпредставления и функций, которые наша ViewGroup хочет реализовать
ViewGroup和子View的大小算出来了之后,接下来就是去摆放了吧,具体怎么去摆放呢?这得根据你定制的需求去摆放了,比如,你想让子View按照垂直顺序一个挨着一个放,或者是按照先后顺序一个叠一个去放,这是你自己决定的。
已经知道怎么去摆放还不行啊,决定了怎么摆放就是相当于把已有的空间”分割”成大大小小的空间,每个空间对应一个子View,我们接下来就是把子View对号入座了,把它们放进它们该放的地方去。
自定义ViewGroup可以参考:Android自定义ViewGroup
Android的包文件APK分为两个部分:代码和资源,所以打包方面也分为资源打包和代码打包两个方面,这篇文章就来分析资源和代码的编译打包原理。
具体说来:
Android apk的安装过程主要氛围以下几步:
可以使用下面的图表示:
概念:Retrofit是一个基于RESTful的HTTP网络请求框架的封装,其中网络请求的本质是由OKHttp完成的,而Retrofit仅仅负责网络请求接口的封装。
原理:App应用程序通过Retrofit请求网络,实际上是使用Retrofit接口层封装请求参数,Header、URL等信息,之后由OKHttp完成后续的请求,在服务器返回数据之后,OKHttp将原始的结果交给Retrofit,最后根据用户的需求对结果进行解析。
1.在retrofit中通过一个接口作为http请求的api接口
public interface NetApi { @GET("repos/{owner}/{repo}/contributors") Call<ResponseBody> contributorsBySimpleGetCall(@Path("owner") String owner, @Path("repo") String repo); }
2.创建一个Retrofit实例
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build();
3.调用api接口
NetApi repo = retrofit.create(NetApi.class); //第三步:调用网络请求的接口获取网络请求 retrofit2.Call<ResponseBody> call = repo.contributorsBySimpleGetCall("username", "path"); call.enqueue(new Callback<ResponseBody>() { //进行异步请求 @Override public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) { //进行异步操作 } @Override public void onFailure(Call<ResponseBody> call, Throwable t) { //执行错误回调方法 } });
retrofit执行的原理如下:
1.首先,通过method把它转换成ServiceMethod。
2.然后,通过serviceMethod,args获取到okHttpCall对象。
3.最后,再把okHttpCall进一步封装并返回Call对象。
首先,创建retrofit对象的方法如下:
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.github.com/") .build();
在创建retrofit对象的时候用到了build()方法,该方法的实现如下:
public Retrofit build() { if (baseUrl == null) { throw new IllegalStateException("Base URL required."); } okhttp3.Call.Factory callFactory = this.callFactory; if (callFactory == null) { callFactory = new OkHttpClient(); //设置kHttpClient } Executor callbackExecutor = this.callbackExecutor; if (callbackExecutor == null) { callbackExecutor = platform.defaultCallbackExecutor(); //设置默认回调执行器 } // Make a defensive copy of the adapters and add the default Call adapter. List<CallAdapter.Factory> adapterFactories = new ArrayList<>(this.adapterFactories); adapterFactories.add(platform.defaultCallAdapterFactory(callbackExecutor)); // Make a defensive copy of the converters. List<Converter.Factory> converterFactories = new ArrayList<>(this.converterFactories); return new Retrofit(callFactory, baseUrl, converterFactories, adapterFactories, callbackExecutor, validateEagerly); //返回新建的Retrofit对象 }
该方法返回了一个Retrofit对象,通过retrofit对象创建网络请求的接口的方式如下:
NetApi repo = retrofit.create(NetApi.class);
retrofit对象的create()方法的实现如下:‘
public <T> T create(final Class<T> service) { Utils.validateServiceInterface(service); if (validateEagerly) { eagerlyValidateMethods(service); } return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { // If the method is a method from Object then defer to normal invocation. if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); //直接调用该方法 } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); //通过平台对象调用该方法 } ServiceMethod serviceMethod = loadServiceMethod(method); //获取ServiceMethod对象 OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args); //传入参数生成okHttpCall对象 return serviceMethod.callAdapter.adapt(okHttpCall); //执行okHttpCall } }); }
Picasso:120K
Glide:475K
Fresco:3.4M
Android-Universal-Image-Loader:162K
图片函数库的选择需要根据APP的具体情况而定,对于严重依赖图片缓存的APP,例如壁纸类,图片社交类APP来说,可以选择最专业的Fresco。对于一般的APP,选择Fresco会显得比较重,毕竟Fresco3.4M的体量摆在这。根据APP对图片的显示和缓存的需求从低到高,我们可以对以上函数库做一个排序。
Picasso < Android-Universal-Image-Loader < Glide < Fresco
Picasso: лучше всего работает вместе с сетевой библиотекой Square, поскольку Picasso может передать часть кэширования сетевого запроса реализации okhttp.
Glide: он имитирует API Пикассо и добавляет множество расширений (таких как gif и другие поддержки). Формат растрового изображения Glide по умолчанию — RGB_565, который требует больше памяти, чем формат Пикассо по умолчанию ARGB_8888. Вполовину меньше; Пикассо кэширует полный размер (кэширует только один тип), тогда как Glide кэширует тот же размер, что и ImageView (то есть 5656 и 128128 - это два кеша).
FB Фреймворк загрузки изображений Fresco: Самое большое преимущество заключается в загрузке растровых изображений ниже 5.0 (минимум 2.3). В системах ниже 5.0 Fresco помещает изображения в специальную область памяти (область Ашмем). Разумеется, когда картинка не отображается, занятая память автоматически освобождается. Это сделает работу приложения более плавной и уменьшит перегрузку, вызванную использованием памяти изображений. Почему говорят, что он ниже 5.0? Потому что после версии 5.0 система по умолчанию сохраняет его в области Ашмем.
Glide может выполнять все функции, которые может выполнить Пикассо, но необходимые настройки другие. Однако размер Picasso значительно меньше, чем у Glide.Если сетевой запрос в самом проекте использует okhttp или доработку (суть все равно okhttp), то рекомендуется использовать Picasso, который будет значительно меньше (работа семейного квадратного ведра). Преимущество Glide — большие потоки изображений, таких как GIF и видео. Если вы создаете видеоприложения, такие как Meipai и Aipai, рекомендуется использовать его.
Оптимизация памяти Fresco ниже 5.0 очень хороша, но цена в том, что объем также очень большой. Fresco>Glide>Picasso
Однако использовать ее также несколько неудобно (небольшое предложение : он Эти функции можно реализовать только с помощью встроенного ImageView, который затруднителен в использовании. Обычно мы модифицируем его в соответствии с Fresco и напрямую используем его растровый слой)
Ссылка на ссылку: https://www.cnblogs.com/kunpengit/p/4001680.html
Gson от Google в настоящее время является наиболее полным анализатором Json. Артефакт Gson изначально был разработан Google в ответ на внутренние потребности Google. Однако с тех пор, как первая версия была публично выпущена в мае 2008 года, она использовалась многими компаниями и пользователями. Приложение Gson в основном состоит из двух функций преобразования: toJson и fromJson. Оно не имеет зависимостей и не требует дополнительных jar-файлов. Оно может работать непосредственно на JDK. Прежде чем использовать этот вид преобразования объектов, вам необходимо создать тип объекта и его члены, прежде чем вы сможете успешно преобразовать строку JSON в соответствующий объект. Пока в классе есть методы get и set, Gson может полностью преобразовывать сложные типы json в bean-компоненты или bean-компоненты в json. Это артефакт синтаксического анализа JSON. Gson безупречен с точки зрения функциональности, но его производительность отстает от FastJson.
Fastjson — это высокопроизводительный процессор JSON, написанный на языке Java, разработанный Alibaba.
Нет зависимостей, нет необходимости в дополнительных jar-файлах, и он может работать непосредственно на JDK. У FastJson возникнут некоторые проблемы при преобразовании сложных типов bean-компонентов в Json. Могут появиться ссылочные типы, вызывающие ошибки преобразования Json, и ссылки необходимо указывать. FastJson использует оригинальный алгоритм для максимального увеличения скорости синтаксического анализа, превосходя все библиотеки json.
Подводя итог сравнению технологии Json, можно сказать, что Gson от Google и FastJson от Alibaba можно использовать параллельно при выборе проектов. Если это просто функциональные требования, а не требования к производительности, вы можете использовать Gson от Google. Если у вас есть Требования к производительности, вы можете использовать Gson для преобразования bean-компонентов в json, чтобы обеспечить точность данных. Используйте FastJson для преобразования Json в bean-компоненты
Ссылка на ссылку — решение для компонентизации Android
Компонентизация: заключается в разделении приложения на несколько модулей.Каждый модуль является компонентом или базовой библиотекой.Зависимости компонентов, некоторые компоненты могут быть отлажены отдельно во время разработки.Компоненты не должны зависеть друг от друга, но могут вызывать друг друга.При окончательном выпуске все компоненты упаковываются в APK в виде библиотеки с помощью основной зависимости проекта приложения.
Каждый бизнес-модуль после компонентизации Это может быть отдельным приложением (isModuleRun=false).При выпуске пакета каждый бизнес-модуль используется как зависимость библиотеки.Это полностью контролируется переменной.В корневом проекте gradle.properties isModuleRun=true. Статус isModuleRun отличается, а приложение загрузки и AndroidManifest различаются, чтобы различать, является ли это независимым APK или библиотекой.
Как разрешить одни и те же ресурсы при создании нескольких модулей Конфликты в именах файлов слияние. Дублирующиеся имена файлов ресурсов бизнес-модуля и BaseModule вызовут конфликты. Решение:
Каждый модуль имеет имя_приложения. Чтобы предотвратить дублирование имен ресурсов, в сборке каждого компонента. Добавьте ресурсный префикс "xxx_ в принудительно проверить префикс имени ресурса в gradle. Исправить префикс ресурса каждого компонента. Однако значение resourcesPrefix может ограничивать только ресурсы в xml и не может ограничивать ресурсы изображений.
How для ссылки на некоторые общие библиотеки и классы инструментов между несколькими модулями
После компонентизации модули изолируются друг от друга, как выполнять переходы и методы пользовательского интерфейса Для вызова вы можете использовать структуры маршрутизации например, Alibaba ARouter или WMRouter от Meituan.
Каждый бизнес-модуль не нуждается в каких-либо зависимостях и может быть пропущен через маршрутизацию, что идеально решает проблему связи между предприятиями.
Мы знаем, что компоненты связаны, поэтому как получить параметры, передаваемые другими модулями при отладке в одиночку
Когда компонент один При запуске каждый модуль формирует свой собственный APK, который означает, что будет несколько приложений. Очевидно, мы не хотим писать так много кода повторно, поэтому нам нужно определить только одно BaseApplication, а другие приложения напрямую наследуют это BaseApplication. Это нормально. Открытые параметры также могут быть определены в BaseApplication.
О том, как реализовать компонентизацию, вы можете узнать по ссылке: Anjuke Android Project Architecture Evolution
Справочная ссылка - Знакомство с плагином
Когда дело доходит до плагина, мы должны упомянуть о проблеме, заключающейся в том, что количество методов превышает 65535. Мы можем решить ее с помощью субподряда Dex, а также решить с помощью плагина -разработка.Концепция плагина заключается в том, что хост-приложение загружает и запускает плагин.
В большом проекте, чтобы иметь четкое разделение труда, часто разные команды отвечают за разные подключаемые приложения, поэтому разделение труда более четкое.Каждый модуль инкапсулирован в разные подключаемые APK, и разные модули могут быть скомпилированы отдельно, что повышает эффективность разработки.
Решите описанную выше проблему превышения лимита количества методов. Онлайн-ошибки можно решить, запустив новые плагины для достижения эффекта «горячего исправления».
Уменьшен размер APK хоста.
Приложения, разработанные как плагины, не могут быть запущены в Google Play, а это значит, что нет зарубежного рынка.
Значение: Физическая единица измерения диагонали мобильного телефона: дюймы ( дюйм), 1 дюйм = 2,54 см
Обычные размеры телефонов Android: 5 дюймов, 5,5 дюймов, 6 дюймов, 6,5 дюймов и т. д.
Значение : Телефон находится в режиме Сумма количества пикселей по горизонтали и вертикали
обычно описывается как «ширина х высота» = AxB экрана. Это означает: экран имеет A пикселей по горизонтали. направлении (ширине) и в вертикальном направлении
(Высота) имеет B пикселей Пример: 1080x1920, то есть 1080 пикселей по ширине и 1920 пикселей по высоте
Единица измерения: пиксель (пиксель), 1 пиксель = 1 пиксель
В чертежах дизайнера пользовательского интерфейса в качестве единой единицы измерения будет использоваться пикс
Общие разрешения для телефонов Android: 320x480, 480x800, 720x1280, 1080x1920
Значение: количество пикселей на дюйм. Единица измерения: dpi (точек на дюйм)
Предполагая, что в устройстве 160 пикселей на дюйм, тогда плотность пикселей экрана устройство =160dpi
6.2.Метод адаптации
1.Поддержка различных размеров экрана: используйте wrap_content, match_parent,weight.Чтобы обеспечить гибкость макета и адаптироваться к различным размерам экрана, следует используйте «wrap_content», «match_parent» для управления шириной и высотой определенных компонентов представления.
2. Используйте относительную компоновку и отключите абсолютную компоновку.
3. Используйте атрибут веса LinearLayout
Что, если наша ширина равна не 0dp (wrap_content и 0dp имеют одинаковый эффект), а match_parent?
Истинное значение android:layout_weight следующее: если представление устанавливает этот атрибут и он действителен, то ширина представления равна исходной ширине (android:layout_width) плюс доля оставшегося пространства. .
С этой точки зрения давайте объясним вышеупомянутое явление. В приведенном выше коде мы устанавливаем ширину каждой кнопки равным match_parent. Предполагая, что ширина экрана равна L, тогда ширина каждой кнопки также должна быть L, а оставшаяся ширина равна L-(L L) = -L .
Вес кнопки1=1, а оставшееся соотношение ширины равно 1/(1 2)= 1/3, поэтому окончательная ширина равна L 1/3*(-L)=2/3L. Button2 аналогичен, окончательная ширина равна L 2/3(-L)=1/3L.
6.3.Сегодняшняя экранная адаптация Toutiao
7.Оптимизация производительности
Справочная ссылка: инструмент мониторинга производительности Android, методы оптимизации памяти, задержки, энергопотребления и размера APK
Оптимизация производительности Android в основном оптимизируется по следующим аспектам:
Стабильная (переполнение памяти, сбой)
Плавность (зависание)
Потребление (энергопотребление, трафик)
(1) Инструмент «Монитор памяти»:
Это инструмент мониторинга памяти, который поставляется с Android Studio. Его можно очень хорошо использовать. Помогите нам выполнить анализ памяти в реальном времени. Перейдя на вкладку «Монитор памяти» в правом нижнем углу Android Studio и открыв инструмент, вы увидите, что более светлый синий цвет представляет собой свободную память, а более темная часть представляет собой используемую память.Из диаграммы тенденций преобразования памяти вы можете определить состояние использования памяти, например Когда объем памяти продолжает увеличиваться, могут возникнуть утечки памяти; когда объем памяти внезапно уменьшается, может произойти сбор мусора и т. д., как показано на рисунке ниже.
Инструмент LeakCanary:
Механизм мониторинга использует Java WeakReference и ReferenceQueue. При упаковке Activity в WeakReference, если объект Activity, обернутый WeakReference, перерабатывается, ссылка WeakReference будет помещена в ReferenceQueue. Благодаря мониторингу содержимое в ReferenceQueue можно использовать для проверки возможности повторного использования Activity (в ReferenceQueue указано, что оно может быть переработано, и утечки нет; в противном случае может быть утечка. LeakCanary выполняет GC один раз. Если ее нет в ReferenceQueue, это будет считаться утечкой. ).
(3) Инструмент Android Lint:
(2) Плавность — оптимизация тормозов
Сценарии тормозов обычно возникают в самых непосредственных аспектах взаимодействия с пользователем. Двумя основными факторами, влияющими на задержку, являются прорисовка интерфейса и обработка данных.
Отрисовка интерфейса: Основная причина в том, что уровень прорисовки глубокий, страница сложная, а обновление необоснованно. По этим причинам в пользовательском интерфейсе чаще появляются зависающие сцены, начальный интерфейс после запуска и рисунок, который переходит на страницу.
###Обработка данных: Причина этого сценария задержки заключается в том, что объем обработки данных слишком велик, что обычно делится на три ситуации: первая — данные обрабатываются в потоке пользовательского интерфейса, а другая — что обработка данных отнимает много ресурсов процессора, из-за чего основной поток не может получить временной интервал.В-третьих, увеличение памяти приводит к частому сбору мусора, что приводит к лагам. ###Когда система Android измеряет, размещает и рисует виды, она работает путем перемещения по количеству представлений. Если высота номера вида слишком велика, это серьезно повлияет на скорость измерения, компоновки и рисования. Google также рекомендует в своей документации API, чтобы высота просмотра не превышала 10 слоев. В текущей версии Google использует RelativeLayout вместо LineraLayout в качестве корневого макета по умолчанию. Цель состоит в том, чтобы уменьшить высоту дерева макета, создаваемого вложением LineraLayout, тем самым повышая эффективность рендеринга пользовательского интерфейса.
Повторное использование макета, используйте метки для повторного использования макета;
Увеличьте скорость отображения, используйте отложенную загрузку представления;
Уменьшите уровни, используйте метки для замены родительского макета;
Обратите внимание, что использование Wrap_content увеличит размер Рассчитать стоимость;
Удалить ненужные атрибуты в элементах управления;
Перерисовка означает, что определенный пиксель на экране отрисовывается слишком много раз в одном и том же кадре. ставка. Если в многоуровневой перекрывающейся структуре пользовательского интерфейса невидимый пользовательский интерфейс также выполняет операции рисования, это приведет к тому, что определенные области пикселей будут отрисовываться несколько раз, что приведет к потере избыточных ресурсов ЦП и графического процессора. Как избежать перерисовки?
Оптимизация макета. Удалите ненужный фон в XML, удалите фон Windows по умолчанию и отобразите фоновые изображения-заполнители по требованию
Оптимизация индивидуального представления. Используйте Canvas.clipRect(), чтобы помочь системе идентифицировать эти видимые области, и рисоваться будет только внутри этой области.
Приложения обычно имеют страницу-заставку SplashActivity, которая оптимизирует макет пользовательского интерфейса страницы-заставки и может обнаруживать потерю кадров посредством рендеринга профиля GPU.
До Android 5.0 проверка энергопотребления приложений была затруднительной и неточной. После 5.0 Google специально представила устройство для получения API информации о энергопотреблении. —— Историк аккумуляторов. Battery Historian — это инструмент анализа энергопотребления системы Android, предоставленный Google. Он визуально отображает процесс энергопотребления мобильного телефона и отображает ситуацию с потреблением путем ввода файла анализа энергопотребления.
Наконец, для справки приведены некоторые методы оптимизации энергопотребления:
Арифметика с плавающей запятой: В компьютерах целые и десятичные числа хранятся в обычных форматах, таких как 1024, 3,1415926 и т. д. У нее нет характеристик, но точность таких чисел невысока, и выражение не является Чтобы иметь универсальное представление чисел, были изобретены числа с плавающей запятой. Представление чисел с плавающей запятой немного похоже на научную запись (.##×10***), ее представление равно 0.*****×10, компьютерная форма: .*** e ±**), где первая звездочка представляет десятичную дробь с фиксированной запятой, то есть чистое десятичное число с целым числом. часть равна 0, и следующая часть. Показательная часть представляет собой целое число с фиксированной точкой. В этой форме можно выразить любое целое или десятичное число. Например, 1024 можно выразить как 0,1024×10^4, что равно 0,1024e 004, а 3,1415926 можно выразить как 0,31415926×10^1, что равно 0,31415926e 001. Это число с плавающей запятой. Операции, выполняемые с числами с плавающей запятой, являются операциями с плавающей запятой. Операции с плавающей запятой более сложны, чем обычные операции, поэтому компьютеры выполняют операции с плавающей запятой гораздо медленнее, чем обычные операции.
(2) Избегайте неправильного использования Wake Lock. Wake Lock — это механизм блокировки, в основном связанный со спящим режимом системы. Пока кто-то удерживает эту блокировку, система не может перейти в спящий режим. Это означает, что моя программа добавила эту блокировку в ЦП. система не будет спать.Цель этого - полностью сотрудничать с работой нашей программы. В некоторых случаях, если вы этого не сделаете, возникнут некоторые проблемы. Например, пакеты Heartbeat для обмена мгновенными сообщениями, такие как WeChat, прекратят доступ к сети вскоре после выключения экрана. Поэтому Wake_Lock широко используется в WeChat. В целях экономии энергии система автоматически переходит в спящий режим, когда процессор не занят задачами. Когда есть задача, которая должна разбудить ЦП для эффективного выполнения, к ЦП будет добавлен Wake_Lock. Распространенная ошибка, которую допускают все, заключается в том, что процессор легко разбудить для работы, но легко забыть выпустить Wake_Lock. (3) Используйте планировщик заданий для управления фоновыми задачами. В Android 5.0 API 21 Google предоставляет компонент под названием JobScheduler API для обработки сценария выполнения задачи в определенный момент времени или при выполнении определенного условия, например, когда пользователь выполняет задачу в определенный момент времени. ночь При отдыхе или когда устройство подключено к адаптеру питания и подключено к WiFi, у него запускается задача загрузки обновлений. Это может повысить эффективность приложений и одновременно снизить потребление ресурсов. (4) Инсталляционный пакет - APK для похудения (1) Структура установочного пакета папка assets. Для хранения некоторых файлов конфигурации и файлов ресурсов ресурсы не будут автоматически генерировать соответствующие идентификаторы, а получат их через интерфейс класса AssetManager. рез. res — это сокращение от ресурса. В этом каталоге хранятся файлы ресурсов. Соответствующий идентификатор будет автоматически сгенерирован и сопоставлен с файлом .R. Используйте идентификатор ресурса непосредственно для доступа. МЕТА-ИНФ. Сохраните информацию о подписи приложения, которая может проверить целостность файла APK. AndroidManifest.xml. Этот файл используется для описания информации о конфигурации приложения Android, регистрационной информации некоторых компонентов, используемых разрешений и т. д.классы.dex. Программа байт-кода Dalvik делает виртуальную машину Dalvik исполняемой.Как правило, приложения Android используют инструмент dx в Android SDK для преобразования байт-кода Java в байт-код Dalvik при упаковке.
resources.arsc. Он записывает отношения сопоставления между файлами ресурсов и идентификаторами ресурсов и используется для поиска ресурсов на основе идентификаторов ресурсов.
Обфускация кода. Используйте инструмент обфускации кода proGuard, входящий в состав IDE, который включает в себя сжатие, оптимизацию, обфускацию и другие функции.
Оптимизация ресурсов. Например, используйте Android Lint для удаления избыточных ресурсов, минимизации файлов ресурсов и т. д.
Оптимизация изображения. Например, используйте инструменты оптимизации PNG для сжатия изображений. Порекомендуйте самый продвинутый инструмент сжатия Googlek — библиотеку с открытым исходным кодом zopfli. Если приложение версии 0 или выше, рекомендуется использовать формат изображения WebP.
Избегайте сторонних библиотек с дублирующими или бесполезными функциями. Например, Baidu Maps можно подключить к базовой карте, iFlytek Voice не требует подключения в автономном режиме, библиотеку изображений Glide\Picasso и т. д.
Разработка плагина. Например, функциональные модули размещаются на сервере и загружаются по требованию, что позволяет уменьшить размер установочного пакета.
Вы можете использовать инструмент обфускации файлов ресурсов WeChat с открытым исходным кодом — AndResGuard. Как правило, размер APK можно сжать примерно до 1 МБ.
Ссылка на ссылку: https://www.jianshu.com/p/03c0fd3fc245
Холодный старт
При запуске приложения в системе нет процесса для приложения.В это время система создаст новый процесс и назначит его приложению
Горячий старт
При запуске приложения в системе уже есть процесс приложения (например: нажмите клавишу «Назад» или клавишу «Домой», хотя приложение завершится, процесс приложения все равно останется в фоновом режиме);
Разница
Холодный старт: в системе нет процесса для приложения, необходимо создать новый процесс и назначить его приложению, поэтому класс приложения будет сначала создается и инициализируется, затем будет создан и инициализирован класс MainActivity (включая серию измерений, макета и чертежа) и, наконец, отображен в интерфейсе. Горячий старт: запуск из существующего процесса. Класс Application не будет создан и инициализирован. Класс MainActivity будет создан и инициализирован напрямую (включая серию измерений, макета и чертежа) и, наконец, отображен в интерфейсе.
Процесс холодного запуска
Fork создает новый процесс в процессе Zygote; создает и инициализирует класс Application, создает MainActivity; раздувает макет, когда все методы onCreate/onStart/onResume go Готово; размер/макет/рисование contentView отображается в интерфейсе.
Оптимизация холодного запуска
Уменьшите рабочую нагрузку в методе onCreate() приложения и первом действии; не позволяйте приложению участвовать в бизнес-операциях; не выполняйте трудоемкие операции в приложении ; Не сохранять данные в приложении в виде статических переменных; Уменьшить сложность и глубину макета;
Архитектура MVP Разработана на основе MVC. В MVP M означает модель, V означает представление, а P означает презентатор.
Уровень модели (модель): в основном используется для получения функций данных, бизнес-логики и моделей сущностей.
Слой представления (представление): соответствует действию или фрагменту, отвечает за частичное отображение представления и взаимодействие с пользователем по бизнес-логике
уровень управления (представитель): отвечает за завершение взаимодействия между представлением Layer и Layer Layer, получить данные в слое M через уровень P и вернуть их на уровень V, чтобы не было связи между слоем V и слоем M.
В MVP уровень Presenter полностью разделяет уровень представления и уровень модели и реализует основную логику программы на уровне Presenter. Presenter не связан напрямую с конкретным слоем представления (активностью), но реализовано через Определить интерфейс для взаимодействия, так что при изменении уровня представления (активности) презентатор все равно может оставаться неизменным. Класс интерфейса уровня представления должен иметь только методы set/get, а также часть интерфейса, отображающего содержимое и пользовательский ввод. Кроме того, не должно быть избыточного содержимого. Слою представления никогда не разрешен прямой доступ к уровню модели. Это самое большое отличие от MVC и основное преимущество MVP.
Android4.4 и более ранних версий с использованием виртуальной машины Dalvik , мы знаем, что в процессе упаковки Apk сначала скомпилирует Java и другие исходные коды в файлы .class через javac, но наша виртуальная машина Dalvik будет выполнять только файлы .dex. В это время dx преобразует файлы .class в Dalvik. виртуальные машины.dex-файл для машинного выполнения. Когда виртуальная машина Dalvik запускается, она сначала преобразует файл .dex в быстро исполняемый машинный код. Из-за проблемы 65535 у нас есть процесс совместной упаковки при холодном запуске приложения. Конечным результатом является наш Приложение запускается медленно.Это функция JIT (Just In Time) виртуальной машины Dalvik.
Виртуальная машина ART — это виртуальная машина Android, использование которой началось только в Android 5.0. Виртуальная машина ART должна быть совместима с характеристиками виртуальной машины Dalvik, но У ART есть очень хорошая функция AOT (заранее), эта функция заключается в том, что когда мы устанавливаем APK, мы напрямую обрабатываем dex в машинный код, который может напрямую использоваться виртуальной машиной ART.Виртуальная машина ART преобразует файл .dex. в файл .oat, который можно запустить напрямую.ART Виртуальная машина по своей сути поддерживает несколько дексов, поэтому процесс синхронизации отсутствует, поэтому виртуальная машина ART значительно повышает скорость холодного запуска приложения.
Преимущества ART:
Ускорение холодного запуска приложения
Улучшение скорости GC
提供功能全面的Debug特性
ART缺点:
APP安装速度慢,因为在APK安装的时候要生成可运行.oat文件
APK占用空间大,因为在APK安装的时候要生成可运行.oat文件
arm处理器
关于ART更详细的介绍,可以参考Android ART详解
熟悉Android性能分析工具、UI卡顿、APP启动、包瘦身和内存性能优化
熟悉Android APP架构设计,模块化、组件化、插件化开发
熟练掌握Java、设计模式、网络、多线程技术
jvm将.class类文件信息加载到内存并解析成对应的class对象的过程,注意:jvm并不是一开始就把所有的类加载进内存中,只是在第一次遇到某个需要运行的类才会加载,并且只加载一次
主要分为三部分:1、加载,2、链接(1.验证,2.准备,3.解析),3、初始化
类加载器包括 BootClassLoader、ExtClassLoader、APPClassLoader
验证:(验证class文件的字节流是否符合jvm规范)
准备:为类变量分配内存,并且进行赋初值
解析:将常量池里面的符号引用(变量名)替换成直接引用(内存地址)过程,在解析阶段,jvm会把所有的类名、方法名、字段名、这些符号引用替换成具体的内存地址或者偏移量。
主要对类变量进行初始化,执行类构造器的过程,换句话说,只对static修试的变量或者语句进行初始化。
范例:Person person = new Person();为例进行说明。
Java编程思想中的类的初始化过程主要有以下几点:
StringBuffer里面的很多方法添加了synchronized关键字,是可以表征线程安全的,所以多线程情况下使用它。
执行速度:
StringBuilder > StringBuffer > String
StringBuilder牺牲了性能来换取速度的,这两个是可以直接在原对象上面进行修改,省去了创建新对象和回收老对象的过程,而String是字符串常量(final)修试,另外两个是字符串变量,常量对象一旦创建就不可以修改,变量是可以进行修改的,所以对于String字符串的操作包含下面三个步骤:
Java对象实例化过程中,主要使用到虚拟机栈、Java堆和方法区。Java文件经过编译之后首先会被加载到jvm方法区中,jvm方法区中很重的一个部分是运行时常量池,用以存储class文件类的版本、字段、方法、接口等描述信息和编译期间的常量和静态常量。
类加载器classLoader,在JVM启动时或者类运行时将需要的.class文件加载到内存中。
执行引擎,负责执行class文件中包含的字节码指令。
本地方法接口,主要是调用C/C++实现的本地方法及返回结果。
内存区域(运行时数据区),是在JVM运行的时候操作所分配的内存区,
主要分为以下五个部分,如下图:
https://www.jianshu.com/nb/12554212
垃圾收集器一般完成两件事
通常,Java对象的引用可以分为4类:强引用、软引用、弱引用和虚引用。
强引用:通常可以认为是通过new出来的对象,即使内存不足,GC进行垃圾收集的时候也不会主动回收。
Object obj = new Object();
软引用:在内存不足的时候,GC进行垃圾收集的时候会被GC回收。
Object obj = new Object(); SoftReference<Object> softReference = new SoftReference<>(obj);
弱引用:无论内存是否充足,GC进行垃圾收集的时候都会回收。
Object obj = new Object(); WeakReference<Object> weakReference = new WeakReference<>(obj);
虚引用:和弱引用类似,主要区别在于虚引用必须和引用队列一起使用。
Object obj = new Object(); ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>(); PhantomReference<Object> phantomReference = new PhantomReference<>(obj, referenceQueue);
引用队列:如果软引用和弱引用被GC回收,JVM就会把这个引用加到引用队列里,如果是虚引用,在回收前就会被加到引用队列里。
引用计数法:给每个对象添加引用计数器,每个地方引用它,计数器就+1,失效时-1。如果两个对象互相引用时,就导致无法回收。
可达性分析算法:以根集对象为起始点进行搜索,如果对象不可达的话就是垃圾对象。根集(Java栈中引用的对象、方法区中常量池中引用的对象、本地方法中引用的对象等。JVM在垃圾回收的时候,会检查堆中所有对象是否被这些根集对象引用,不能够被引用的对象就会被垃圾回收器回收。)
常见的垃圾回收算法有:
标记:首先标记所有需要回收的对象,在标记完成之后统计回收所有被标记的对象,它的标记过程即为上面的可达性分析算法。
清除:清除所有被标记的对象
缺点:
效率不足,标记和清除效率都不高
空间问题,标记清除之后会产生大量不连续的内存碎片,导致大对象分配无法找到足够的空间,提前进行垃圾回收。
复制回收算法
将可用的内存按容量划分为大小相等的2块,每次只用一块,当这一块的内存用完了,就将存活的对象复制到另外一块上面,然后把已使用过的内存空间一次清理掉。
缺点:
将内存缩小了原本的一般,代价比较高
大部分对象是“朝生夕灭”的,所以不必按照1:1的比例划分。
现在商业虚拟机采用这种算法回收新生代,但不是按1:1的比例,而是将内存区域划分为eden 空间、from 空间、to 空间 3 个部分。
其中 from 空间和 to 空间可以视为用于复制的两块大小相同、地位相等,且可进行角色互换的空间块。from 和 to 空间也称为 survivor 空间,即幸存者空间,用于存放未被回收的对象。
在垃圾回收时,eden 空间中的存活对象会被复制到未使用的 survivor 空间中 (假设是 to),正在使用的 survivor 空间 (假设是 from) 中的年轻对象也会被复制到 to 空间中 (大对象,或者老年对象会直接进入老年带,如果 to 空间已满,则对象也会直接进入老年代)。此时,eden 空间和 from 空间中的剩余对象就是垃圾对象,可以直接清空,to 空间则存放此次回收后的存活对象。这种改进的复制算法既保证了空间的连续性,又避免了大量的内存空间浪费。
在老年代的对象大都是存活对象,复制算法在对象存活率教高的时候,效率就会变得比较低。根据老年代的特点,有人提出了“标记-压缩算法(Mark-Compact)”
标记过程与标记-清除的标记一样,但后续不是对可回收对象进行清理,而是让所有的对象都向一端移动,然后直接清理掉端边界以外的内存。
这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。
根据对象存活的周期不同将内存划分为几块,一般是把Java堆分为老年代和新生代,这样根据各个年代的特点采用适当的收集算法。
新生代每次收集都有大量对象死去,只有少量存活,那就选用复制算法,复制的对象数较少就可完成收集。
老年代对象存活率高,使用标记-压缩算法,以提高垃圾回收效率。
程序在启动的时候,并不会一次性加载程序所要用的所有class文件,而是根据程序的需要,通过Java的类加载机制(ClassLoader)来动态加载某个class文件到内存当中的,从而只有class文件被载入到了内存之后,才能被其它class所引用。所以ClassLoader就是用来动态加载class文件到内存当中用的。
每个ClassLoader实例都有一个父类加载器的引用(不是继承关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但是可以用做其他ClassLoader实例的父类加载器。
当一个ClassLoader 实例需要加载某个类时,它会试图在亲自搜索这个类之前先把这个任务委托给它的父类加载器,这个过程是由上而下依次检查的,首先由顶层的类加载器Bootstrap CLassLoader进行加载,如果没有加载到,则把任务转交给Extension CLassLoader视图加载,如果也没有找到,则转交给AppCLassLoader进行加载,还是没有的话,则交给委托的发起者,由它到指定的文件系统或者网络等URL中进行加载类。还没有找到的话,则会抛出CLassNotFoundException异常。否则将这个类生成一个类的定义,并将它加载到内存中,最后返回这个类在内存中的Class实例对象。
JVM在判断两个class是否相同时,不仅要判断两个类名是否相同,还要判断是否是同一个类加载器加载的。
避免重复加载,父类已经加载了,则子CLassLoader没有必要再次加载。
考虑安全因素,假设自定义一个String类,除非改变JDK中CLassLoader的搜索类的默认算法,否则用户自定义的CLassLoader如法加载一个自己写的String类,因为String类在启动时就被引导类加载器Bootstrap CLassLoader加载了。
关于Android的双亲委托机制,可以参考android classloader双亲委托模式
Java集合类主要由两个接口派生出:Collection和Map,这两个接口是Java集合的根接口。
Collection接口是集合类的根接口,Java中没有提供这个接口的直接的实现类。但是却让其被继承产生了两个接口,就是 Set和List。Set中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。
Map是Java.util包中的另一个接口,它和Collection接口没有关系,是相互独立的,但是都属于集合类的一部分。Map包含了key-value对。Map不能包含重复的key,但是可以包含相同的value。
List,Set都是继承自Collection接口,Map则不是;
List特点:元素有放入顺序,元素可重复; Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法;
LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的;
HashMap是非线程安全的,HashTable是线程安全的;
Vector是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结果。而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;
两个都是采用的线性连续空间存储元素,但是当空间不足的时候,两个类的增加方式是不同。
Vector可以设置增长因子,而ArrayList不可以。
Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。
HashSet底层通过HashMap来实现的,在往HashSet中添加元素是
public boolean add(E e) { return map.put(e, PRESENT)==null; } // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object();
在HashMap中进行查找是否存在这个key,value始终是一样的,主要有以下几种情况:
HashMap 非线程安全,基于哈希表(散列表)实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,您可以调优初始容量和负载因子。其中散列表的冲突处理主要分两种,一种是开放定址法,另一种是链表法。HashMap的实现中采用的是链表法。
TreeMap:非线程安全基于红黑树实现,TreeMap没有调优选项,因为该树总处于平衡状态
当数值范围为-128~127时:如果两个new出来Integer对象,即使值相同,通过“”比较结果为false,但两个对象直接赋值,则通过“”比较结果为“true,这一点与String非常相似。
当数值不在-128~127时,无论通过哪种方式,即使两个对象的值相等,通过“”比较,其结果为false;
当一个Integer对象直接与一个int基本数据类型通过“”比较,其结果与第一点相同;
Integer对象的hash值为数值本身;
@Override public int hashCode() { return Integer.hashCode(value); }
在Integer类中有一个静态内部类IntegerCache,在IntegerCache类中有一个Integer数组,用以缓存当数值范围为-128~127时的Integer对象。
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。
泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
它提供了编译期的类型安全,确保你只能把正确类型的对象放入 集合中,避免了在运行时出现ClassCastException。
使用Java的泛型时应注意以下几点:
Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为类型擦除。
泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如 List在运行时仅用一个List来表示。这样做的目的,是确保能和Java 5之前的版本开发二进制类库进行兼容。你无法在运行时访问到类型参数,因为编译器已经把泛型类型转换成了原始类型。
限定通配符对类型进行了限制。
Одним из них является , который устанавливает верхнюю границу типа, гарантируя, что тип должен быть подклассом T.
Другой - , который устанавливает верхнюю границу type, гарантируя, что тип должен быть подклассом родительского класса T., чтобы установить нижнюю границу типа.
С другой стороны, > представляет собой неполный подстановочный знак, поскольку > можно заменить любым типом.
Например, List расширяет Number> может принимать List или List.
Для тех, кто не знаком с дженериками, этот вопрос о дженериках в Java может показаться запутанным, потому что на первый взгляд String — это своего рода объект, поэтому List следует использовать везде, где нужен List. ,Но это не так. случай. Это приведет к ошибкам компиляции. Если вы подумаете об этом дальше, вы обнаружите, что для Java имеет смысл сделать это, поскольку List может хранить объекты любого типа, включая String, Integer и т. д., но List можно использовать только для хранения строк.
Array на самом деле не поддерживает дженерики, поэтому Джошуа Блох предложил использовать List вместо Array в книге «Эффективная Java», потому что List может обеспечить время компиляции. гарантирует безопасность типов, а Array — нет.
Основное различие между примитивными типами и параметризованными типами заключается в том, что компилятор не обеспечивает безопасность типов для примитивных типов во время компиляции. Проверьте , но будет проверен тип с параметрами. Используя в качестве типа Object, вы можете сообщить компилятору, что метод может принимать любой тип объекта, например String или Integer. Тестовая точка этого вопроса заключается в правильном понимании примитивных типов в дженериках. Второе отличие между ними заключается в том, что вы можете передать любой тип с параметрами в примитивный тип List, но вы не можете передать List в метод, который принимает List, потому что произойдет ошибка компиляции.
List> — это список неизвестного типа, а List на самом деле является списком любого типа. Вы можете назначить List, List к List>, но не можете назначить List к List.
Механизм отражения JAVA заключается в том, что в рабочем состоянии для любого класса могут быть известны все свойства и методы класса; Любой объект может вызывать любой из своих методов; это динамическое получение информации и функция динамического вызова методов объекта называются механизмом отражения языка Java.
Механизм отражения Java в основном предоставляет следующие функции: определение класса, к которому принадлежит любой объект во время выполнения; создание объекта любого класса во время выполнения; определение любого класса во время выполнения. ; Переменные-члены и методы, принадлежащие классу; вызов методов любого объекта во время выполнения; генерация динамических прокси.
Вы, должно быть, очень хорошо знакомы со словом «агент», потому что вы много сталкивались с ним в реальности.На самом деле, вещи в реальности могут отражать абстрактный процесс узор очень яркий и интуитивно понятный.Природа. Разве дома сейчас не очень шумно? Давайте возьмем в качестве примера дом, чтобы снять завесу свободы воли.
Предположим, у вас есть дом, который вы хотите продать. Один из способов – разместить информацию о продаже непосредственно в Интернете, а затем напрямую пригласить людей, которые хотят купить дом, осмотреть дом, передать право собственности и т. д. до тех пор, пока он не будет продан. дом продан, но вам может быть сложно это сделать. Если вы заняты, у вас нет времени заниматься этими вещами, поэтому вы можете пойти к посреднику и позволить посреднику помочь вам справиться с этими тривиальными вещами. Посредник на самом деле является вашим агентом. Изначально это нужно было делать вам, но теперь посредник помогает вам справиться с этим один за другим. Для покупателя нет разницы между прямыми сделками с вами и прямыми сделками с посредником. Покупатель может даже не знать о ваших существование.Это фактически часть агентства.Максимальная выгода.
Далее давайте подробно рассмотрим, почему вы не покупаете дом напрямую, а нуждаетесь в агенте? Фактически, один вопрос точно отвечает на вопрос, когда использовать режим прокси.
Причина 1: Вы можете работать за городом, и покупатели жилья не смогут найти вас для прямых сделок.
Соответствует конструкции нашей программы: клиент не может напрямую управлять реальным объектом. Так почему же это нельзя сделать напрямую? Одна ситуация заключается в том, что объект, который вам нужно вызвать, находится на другой машине, и вам необходимо получить к нему доступ по сети. Если вы вызываете его напрямую путем кодирования, вам нужно обработать сетевое подключение, упаковать, распаковать и другие очень сложные шаги, Итак, чтобы упростить обработку клиента, мы используем режим прокси для установки прокси для удаленного объекта на клиенте.Клиент вызывает прокси так же, как вызов локального объекта, а затем прокси связывается с фактическим объектом.Для клиента, может не быть. Такое ощущение, что вызываемая вещь находится на другом конце сети. На самом деле именно так и работает веб-сервис. В другом случае, хотя объект, который вы хотите вызвать, является локальным, поскольку вызов занимает очень много времени, вы боитесь повлиять на ваши обычные операции, поэтому специально находите агента, чтобы справиться с этой трудоемкой ситуацией.Один из самых простых чтобы понять, находится в Word. Установлено большое изображение. Когда слово открывается, мы должны загрузить содержимое внутри и открыть его вместе. Однако, если пользователь ждет, пока загрузится большое изображение, прежде чем открыть Word, пользователь, возможно, уже перешел в ожидании, поэтому мы можем настроить прокси и позволить прокси медленно открывать изображение, не затрагивая исходную функцию открытия Word. Поясню: я просто догадался, что Word может это сделать, но как именно это делается, я не знаю.
Причина 2: Вы не знаете, как пройти процедуры перевода, или помимо того, что вы можете сделать сейчас, вам нужно сделать и другие дела для достижения цели.
Соответствует дизайну нашей программы: помимо функций, которые может предоставить текущий класс, нам также необходимо добавить некоторые другие функции. Самая простая ситуация, о которой можно подумать, - это фильтрация разрешений. У меня есть класс, который занимается определенным бизнесом, но из соображений безопасности только определенные пользователи могут вызывать этот класс. В настоящее время мы можем создать прокси-класс этого класса, требующий всех запросов для прохождения через этот прокси-класс вынесет решение о разрешении. Если это безопасно, будет вызван бизнес фактического класса для начала обработки. Некоторые люди могут спросить, зачем мне добавлять дополнительный прокси-класс? Мне просто нужно добавить фильтрацию разрешений к методу исходного класса, верно? В программировании существует проблема принципа единства классов.Этот принцип очень прост, то есть функция каждого класса максимально едина. Почему он должен быть одиночным? Потому что только класс с одной функцией с наименьшей вероятностью будет изменен. Возьмем только что пример: если вы поместите решение о разрешении в текущий класс, текущий класс должен нести ответственность за свою собственную бизнес-логику. , Также отвечает за решение о разрешении, есть две причины для изменения этого класса. Теперь, если правила разрешений меняются, этот класс должен быть изменен. Очевидно, это не очень хороший дизайн.
Ладно, я довольно много рассказал о принципах, если я буду продолжать говорить об этом бесконечно, то все могут кидать кирпичи. Ха-ха, давайте посмотрим, как реализовать агентство дальше.
https://zhuanlan.zhihu.com/p/27005757?utm_source=weibo&utm_medium=social
http://crazyandcoder.tech/2016 /14/09/алгоритм и структура данных Android - сортировка/
Сортировка имеет внутреннюю сортировку и внешнюю сортировку.Внутренняя сортировка предназначена для сортировки записей данных в памяти, а внешняя сортировка Это Причина в том, что отсортированные данные очень велики и не могут вместить все отсортированные записи одновременно.Во время процесса сортировки требуется доступ к внешней памяти.
Идея:
Сортировка первого числа и второго числа, а затем формирование упорядоченной последовательности
Сортировка третьего числа Числа вставлен в него, чтобы сформировать новую упорядоченную последовательность.
Повторите второй шаг для четвертого числа, пятого числа... до последнего числа.
Код:
Сначала задайте количество вставок, то есть количество циклов, для (int i=1;i
Ссылка: Некоторые шаблоны проектирования в разработке Android
Отдельные случаи в основном делятся на: ленивые люди Тип синглтона, синглтон в стиле Hungry, зарегистрированный синглтон.
Особенности:
В компьютерных системах пулы потоков, кэши, объекты журналов, диалоговые окна, принтеры и т. д. часто проектируются как одиночные.
Singleton предотвращает создание экземпляра класса извне, ограничивая метод создания частным. В пределах одной виртуальной машины единственный экземпляр Singleton может передавать только метод getInstance(). . доступ. (На самом деле, можно создавать экземпляры классов с помощью частных конструкторов с помощью механизма отражения Java, который по сути делает недействительными все одноэлементные реализации Java.
Это небезопасно для потоков и является параллельным. Очень вероятно, что есть будет несколько экземпляров Singleton. Для достижения потокобезопасности есть следующие три способа:
1. Добавить синхронизацию в метод getInstance
2. Двойная проверка блокировки
3. Статический внутренний класс
По сравнению с первыми двумя методами, этот метод не только обеспечивает потокобезопасность, но и позволяет избежать снижения производительности, вызванного синхронизацией.
В стиле Hungry Han уже создан статический объект для использования системой при создании класса, и он не изменится в будущем, поэтому он по своей сути безопасен для системы.
The above is the detailed content of The most detailed sharing of Android interview questions. For more information, please follow other related articles on the PHP Chinese website!