- 字符串反转
写出一个程序,接受一个字符串,然后输出该字符串反转后的字符串。例如:输入 abcd,输出:dcba
public String reverseString(String s) {
char[] chars = s.toCharArray();
char temp;
int length = chars.length;
for (int i = 0 ,j = length -1; i < length / 2 ; i++,j--) {
temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
return new String(chars);
}
我们只需要遍历char数组中一半的元素就可以了,数组长度为奇数时中间的一个元素不需要管,而int类型除以2正好没有带上中间的元素。
参考文章https://blog.csdn.net/zhwyj1019/article/details/81876505
- 字符串匹配问题
对于字符串str,其中绝对不含有字符’.’和‘’。再给定字符串exp,其中可以含有’.’或’‘’,’’字符不能是exp的首字符,并且任意两个’‘字符不相邻。exp中的’.’代表任何一个字符,exp中的’’表示’‘的前一个字符可以有0个或者多个。请写一个函数,判断str是否能被exp匹配(注意:输入的数据不保证合法,但只含小写字母和‘.’和‘*’)。
递归版本:
class Solution {
public boolean isMatch(String s, String p) {
if(s == null || p == null)
return false;
char[] str = s.toCharArray();
char[] pat = p.toCharArray();
return isValid(str, pat) ? process(str, pat, 0, 0):false;
}
public boolean isValid(char[] s, char[] p){//判断输入是否有效
for(int i = 0; i < s.length; i++){
if(s[i] == '*' || s[i] == '.')
return false;
}
for(int i = 0; i < p.length; i++){
if(p[i] == '*' &&(i == 0 || p[i-1] == '*'))
return false;
}
return true;
}
public boolean process(char[] s, char[] p, int si, int pi){//判断是否匹配
if(pi == p.length)
return si == s.length;
if(pi +1 == p.length || p[pi+1] != '*'){
return si != s.length && (p[pi] == s[si] || p[pi] == '.') && process(s,p,si+1,pi+1);
}
while(si != s.length && (p[pi] == s[si] || p[pi] == '.')){
if(process(s,p,si,pi+2))
return true;
si++;
}
return process(s,p,si,pi+2);
}
}
首先,isValid函数判断输入是否有效,即字符串s不能包含’‘和’.’,字符串p中’‘字符前不能为空或者字符’*’
判断函数process用递归判断,pi,si分别标识字符串p和s的所在位置,即判断字符串p的pi位置之后的字符串是否能模式识别字符串s的si位置之后的字符串。
如果p的pi+1位置不是’*'的话,只需要判断现在所处的字符是否匹配,并且再继续往下判断si+1和pi+1位置后的字符串。
如果p的pi+1位置是’‘的话,两种情况:一是’‘代表0次,我们判断p的pi+2位置之后和s的si位置之后即可;二是’*'代表非0次,我们保持pi不动,si向后移直到跳出循环即可。
原文链接:https://blog.csdn.net/wannuoge4766/article/details/89211379
-
选择题
Service中如何实现更改Activity界面元素(B)
A . 通过把当前activity对象传递给service对象
B . 通过向Activity发送广播
C . 通过Context对象更改Activity界面元素
D . 可以在Service中,调用Activity的方法实现更改界面元素 -
选择题
下列哪些语句关于内存回收的说明是正确的? (B)
A.程序员必须创建一个线程来释放内存
B.内存回收程序负责释放无用内存
C.内存回收程序允许程序员直接释放内存
D.内存回收程序可以在指定的时间释放内存对象 -
选择题
下面关于Android dvm的进程和Linux的进程,应用程序的进程说法正确的是(D)
A. DVM指dalvik的虚拟机.每一个Android应用程序都在它自己的进程中运行,不一定拥有一个独立 的Dalvik虚拟机实例.而每一个DVM都是在Linux中的一个进程,所以说可以认为是同一个概念.
B. DVM指dalvik的虚拟机.每一个Android应用程序都在它自己的进程中运行,不一定拥有一个独立的Dalvik虚拟机实例.而每一个DVM不一定都是在Linux 中的一个进程,所以说不是一个概念.
C. DVM指dalvik的虚拟机.每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的Dalvik虚拟机实例.而每一个DVM不一定都是在Linux 中的一个进程,所以说不是一个概念
D. DVM指dalvik的虚拟机.每一个Android应用程序都在它自己的进程中运行,都拥有一个独立的 Dalvik虚拟机实例.而每一个DVM都是在Linux 中的一个进程,所以说可以认为是同一个概念. -
有序广播和无序广播的区别
1.无序广播
通过Context.sendBroadcast()方法来发送,它是完全异步的。
所有的receivers(接收器)的执行顺序不确定,因此所有的receivers(接收器)接收broadcast的顺序不确定。
这种方式效率更高,但是BroadcastReceiver无法使用setResult系列、getResult系列及abortbroadcast(中止)系列API。
广播不能被终止,数据不能被修改。
2.有序广播
有序广播,即从优先级别最高的广播接收器开始接收,接收完了如果没有丢弃,就下传给下一个次高优先级别的广播接收器进行处理,依次类推,直到最后。如果多个应用程序设置的优先级别相同,则谁先注册的广播,谁就可以优先接收到广播。通过Context.sendorderBroadCast()方法来发送,sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras);,其中的参数resultReceiver,可以自己重写一个类,作为一个最终的receive 最后都能够接收到广播,最终的receiver 不需要再清单文件里面配置,initialData可以作为传输的数据
广播可以被终止,数据传输过程中可以被修改。
参考 https://blog.csdn.net/ldc_123/article/details/52801218
- 全局广播和本地广播的区别(自我拓展:本地广播和全局广播的实现原理)
1、本地广播:发送的广播事件不被其他应用程序获取,也不能响应其他应用程序发送的广播事件。本地广播只能被动态注册,不能静态注册。动态注册或方法时需要用到LocalBroadcastManager。
2、全局广播:发送的广播事件可被其他应用程序获取,也能响应其他应用程序发送的广播事件(可以通过 exported–是否监听其他应用程序发送的广播 在清单文件中控制) 全局广播既可以动态注册,也可以静态注册。
参考https://blog.csdn.net/look_future/article/details/79672760
- 静态广播和动态广播的区别
动态注册和静态注册的区别:
- 动态注册的广播会受Activity的生命周期的影响, 当Activity销毁的时候,广播就失效了。
- 而静态注册的广播,即使Activity销毁了,仍然可以收到广播。更牛掰的是即使杀死进程,仍然可以收到广播。
参考 https://www.cnblogs.com/lang-yu/p/6170325.html
- 冷启动和热启动,关于启动时间的日子打印应该在哪个方法里面
这个题目其实是考察冷启动和热启动的不同流程:
1.冷启动和热启动的概念:
冷启动:
在启动应用时,系统中没有该应用的进程,这时系统会创建一个新的进程分配给该应用;
热启动:
在启动应用时,系统中已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程还是保留在后台);2.冷启动、热启动的区别
冷启动:系统没有该应用的进程,需要创建一个新的进程分配给应用,所以会先创建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。
热启动: 从已有的进程中来启动,不会创建和初始化Application类,直接创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。
3.冷启动时间的计算
API19 之后,系统会出打印日志输出启动的时间;
冷启动时间 = 应用启动(创建进程) —> 完成视图的第一次绘制(Activity内容对用户可见);
4.冷启动流程
Zygote进程中fork创建出一个新的进程;
创建和初始化Application类、创建MainActivity;
inflate布局、当onCreate/onStart/onResume方法都走完;
contentView的measure/layout/draw显示在界面上;
总结:
Application构造方法 –> attachBaseContext() –> onCreate() –> Activity构造方法 –> onCreate() –> 配置主题中背景等属性 –> onStart() –> onResume() –> 测量布局绘制显示在界面上。
- 冷启动和热启动的时间是因为 app 业务影响,还是因为系统影响。(各自的启动时间受什么影响?)
这个题目承接上面的问题,考察的是冷启动的优化问题:冷启动受业务 app 的影响,我们应该:
1.减少在Application和第一个Activity的onCreate()方法的工作量;
2.不要让Application参与业务的操作;
3.不要在Application进行耗时操作;
4.不要以静态变量的方式在Application中保存数据;
5.减少布局的复杂性和深度;
参考 >https://www.jianshu.com/p/eb3b410cce49
- MVVM 中是如何进行数据绑定的
MVVM 中的数据绑定依赖 DataBinding,分为单向绑定和双向绑定。
MVVM是更节省的设计模式,能实现双向的数据绑定。
1.单向绑定是指View层(如EditText)上的数据改变会实时更新到Model层JavaBean中对应的属性值(如username)上。或者,Model层的数据改变会实时更新到View层上的显示,这样我们称之为单向的数据绑定。
2.而双向绑定呢,是指Model层和View层,无论那层数据改变都会实时更新到对方,Model层数据改变会更新View,同样,View层数据改变会更新Model,这样就称之为双向的数据绑定。
- 消息传递机制 (Handler)
1.线程间通讯 ——— Handler,HandlerThread等。
2.组件间通信 ——— BroadcastReceiver,接口回调等。
3. 第三方通信 ——— EventBus,rxBus
4.进程间通信 ——— Content Provider ,Broadcast ,AIDL等。
5.长连接推送 ——— WebSocket,XMPP等。
参考https://blog.csdn.net/haoxuhong/article/details/80103030
Handler
一个Android应用程序被创建的时候都会创建一个UI主线程,但是有时我们会有一些比较耗时的操作,为了防止阻塞UI主线程,我们会将耗时的操作放到子线程中进行处理,处理完之后操作UI,但是Android不允许子线程操作UI,违背了Android单线程模型的原则(即 Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行),所以Android通过Handler消息机制来实现线程之间的通讯。
Handler 机制的主要角色:
1.Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。
2.Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。
3.MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取
4.Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。
5.Thread:线程,负责调度整个消息循环,即消息循环的执行场所。
Handler 机制的主要运用:
sendEmptyMessage(int);//发送一个空的消息
sendMessage(Message);//发送消息,消息中可以携带参数
sendMessageAtTime(Message, long);//未来某一时间点发送消息
sendMessageDelayed(Message, long);//延时Nms发送消息
post(Runnable);//提交计划任务马上执行
postAtTime(Runnable, long);//提交计划任务在未来的时间点执行
postDelayed(Runnable, long);//提交计划任务延时Nms执行
Handler机制扩展:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
以上也可以从子线程切换到主线程。
HandlerThread:
HandlerThread本质上就是一个普通Thread,只不过内部建立了Looper.
HandlerThread 的用法
//创建一个线程,线程名字:handler-thread
myHandlerThread = new HandlerThread( "handler-thread") ;
//开启一个线程
myHandlerThread.start();
//在这个线程中创建一个handler对象 主要这个handler是在子线程中循环接受消息的
handler = new Handler( myHandlerThread.getLooper() ){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
Log.d( "handler " , "消息: " + msg.what + " 线程: " + Thread.currentThread().getName() ) ;
}
};
//在主线程给handler发送消息
handler.sendEmptyMessage( 1 ) ;
new Thread(new Runnable() {
@Override
public void run() {
//在子线程给handler发送数据
handler.sendEmptyMessage( 2 ) ;
}
}).start() ;
}
@Override
protected void onDestroy() {
super.onDestroy();
//释放资源
myHandlerThread.quit() ;
}
Looper的 quit 方法或 quitSafely 方法
相同点:
调用后,将不再接受新的事件加入消息队列。
不同点
当我们调用Looper的quit方法时,实际上执行了MessageQueue中的removeAllMessagesLocked方法,该方法的作用是把MessageQueue消息池中所有的消息全部清空,无论是延迟消息(延迟消息是指通过sendMessageDelayed或通过postDelayed等方法发送的需要延迟执行的消息)还是非延迟消息。
当我们调用Looper的quitSafely方法时,实际上执行了MessageQueue中的removeAllFutureMessagesLocked方法,通过名字就可以看出,该方法只会清空MessageQueue消息池中所有的延迟消息,并将消息池中所有的非延迟消息派发出去让Handler去处理,quitSafely相比于quit方法安全之处在于清空消息之前会派发所有的非延迟消息。
无论是调用了quit方法还是quitSafely方法只会,Looper就不再接收新的消息。即在调用了Looper的quit或quitSafely方法之后,消息循环就终结了,这时候再通过Handler调用sendMessage或post等方法发送消息时均返回false,表示消息没有成功放入消息队列MessageQueue中,因为消息队列已经退出了。
需要注意的是Looper的quit方法从API Level 1就存在了,但是Looper的quitSafely方法从API Level 18才添加进来。
-
如何在子线程中刷新视频流
-
怎样开启一个多进程
在Android中说多进程一般是指一个应用中存在多个进程,在Android中使用多进程只有一种方法:给四大组件在AndroidMenifest中指定android:process属性,除此之外别无他法(通过JNI在native层去fork一个进程也可以,不常用,不做介绍),所以我们不能给一个线程或者实体类指定其运行时所在的进程。
开启多进程
<activity
android:name="com.zhong.ActivityA" />
<activity
android:name="com.zhong.ActivityB"
android:process=":remote" />
<activity
android:name="com.zhong.ActivityC"
android:process="com.zhong.remote" />
上面三个Activity中:
ActivityA:未指明android:process属性,它允许在默认进程中,进程名为包名,即com.zhong;
ActivityB:设置android:process=":remote",系统会为它创建一个单独的进程,进程名为包名+:remote,即com.zhong:remote,相当于android:process=“com.zhong:remote”;
ActivityC:设置android:process=“com.zhong.remote”,系统会为它创建一个单独的进程,进程名为process设置的值,即com.zhong.remote
":remote"和"com.zhong.remote"区别:
“:remote"中”:"是指要在当前进程名前面附近包名的简写,“com.zhong.remote"是一种完整的写法,进程名不会再附加包名;其次,”:"是属于当前应用的私有进程,"com.zhong.remote"是全局进程,全局进程其他应用可以通过ShareUID方式跑在同一进程中(前提是两个应用ShareUID相同并且签名相同),这两个应用可以相互访问对方私有数据。
多进程运行机制
由于Android为每个进程分配独立的虚拟机,所以不同虚拟机访问同一个类会产生不同的副本,因此运行在不同进程中的组件无法通过内存共享数据,所以一个应用使用多进程会出现一下几个问题:
1.静态成员和单例模式完全失效(不是同一块内存,会产生不同的副本)
2.线程同步机制完全失效(不是同一块内存,所以对象也不是同一个,因此类锁、对象锁也不是同一个,不能保证线程同步)
3.SharedPreferences 可靠性下降(SharedPreferences不支持多个进程同时写,会有一定的几率丢失数据)
4.Application 多次创建(Android为每个进程分配独立的虚拟机,这个过程其实就是启动一个应用,所以Application会被创建多次)
多进程小结:
Android中使用多进程一般是来分担主进程的内存压力,应用越做越大,需要的内存也越来越多,讲一些独立的组件放在不同的进程中,这样可以减轻主进程的内存负担;还有就是启动一个Service,做一些守护或者耗时的操作。
参考 Android多进程模式
- Service与 Activity 怎样交互
1.使用接口回调方式,activity实现相应的接口,service通过接口进行回调,比较灵活
2.使用广播
参考 Android—Service与Activity的交互
- 直播视频流是在哪个线程更新的,怎么做到的,为什么?
是在子线程中更新的,使用 SurfaceView 来实现的。为何SurfaceView能够在非UI线程中刷新界面?
SurfaceView与View的刷新方法都是一样的,通过lockCanvas和unlockCanvasAndPost方法来进行画的,但SurfaceView能在UI线程中刷新,也能在其它线程中刷新,而View只能在UI线程中刷新,View的刷新有一个checkThread(在ViewRootImp.java中)的判断,如果不是在UI线程中就会抛异常, 这是google人为这样设计的,不让其它线程刷新View,SurfaceView就不会进行判断,这样它就可以在其它线程中进行刷新。
- 当前 Activity 持有哪些窗体
其实这个问题想问的是 Android - Activity 与 Window 与 View 之间的关系。
Window 是什么?
Window 是 Android 中窗口的宏观定义,主要是管理 View 的创建,以及与 ViewRootImpl 的交互,将 Activity 与 View 解耦。
Activity 与 PhoneWindow 与 DecorView 之间什么关系?
一个 Activity 对应一个 Window 也就是 PhoneWindow,一个 PhoneWindow 持有一个 DecorView 的实例,DecorView 本身是一个 FrameLayout。
参考一篇文章看明白 Activity 与 Window 与 View 之间的关系
- 页面间数据通信有哪些方式
1.如果页面之间有直接关系,如Activity和在它之内的Fragment,可以直接通过接口的调用来传递数据。
优势:直接,方便。 劣势:代码耦合性较高
2.如果是两个Activity之间传递数据,有界面切换的过程的话,可以用startActivity
或startActivityForResult。用其中的intent参数携带数据。
优势:一般用于初始化Activity和调用系统功能
3.如果页面之间传递数据没有页面切换的过程,可以通过广播的方式,sendBroadcast(intent);
要接受数据的页面注册这个广播就行了。
优势:代码耦合性低,易重构,适用范围广。缺点:数据需要序列化和反序列化,代码较多
4.通过存储介质来分享数据,如页面A将数据存入数据库,SharedPreferences文件,Internet。页面B通过读取它们来得到数据。
优势:数据保存时间长,不受到界面生命周期的影响 缺点:读取速度较慢,需要异步操作
5.采用事件总线的方式,注册和接收事件(数据),其中的代表者是EventBus,页面需要指定和注册接收事件的类型
优势:不用序列化数据,适用范围大 缺点:需要学习使用。
- recylcerView 相比 listview 有哪些优点
优缺点
1.ListView相比RecycleView的优点
a.ListView实现添加HeaderView和FooderView有直接的方法
b.分割线可以直接设置
c.ListView实现onItemClickListence和onItemLongClickListence有直接的方法
2.RecyclerView相比ListView的优点
a.封装了ViewHodler,效率更高
b.可以添加增删Item动画、侧滑功能等
c.支持局部更新,可见才更新,不可见不更新
d.插件式实现,各个功能模块化,解耦性强,使用起来更方便
3.但RecyclerView不会替代ListView,因为使用场景不同;但是会替代很多开元框架:横向ListView、瀑布流等
Android RecyclerView的使用及和ListView比较的优缺点
ListView 与 RecyclerView 简单对比
RecyclerView与ListView的异同
RecyclerView 和 ListView 性能和效果区别
-
平时碰到过哪些内存内存优化
OOM、ANR、ML
Android - 性能优化-内存优化 -
比较下 MVP 和 mvvm
MVP 缺点:
1.会增加大量的模板代码
2.更新一个很小的 UI 元素,也需要层层调用,流程比较绕
MVVM 缺点:
1.在布局文件xml中加入了很多逻辑代码,违背了展示和逻辑分离的原则,增加了复杂度,难以阅读和维护
2.单纯的MVVM模式只能实现简单的UI更新,无法实现诸如列表更新的功能,以及加载完成网络数据后弹一个toast之类的功能
3.不好定位 bug,很多问题无法直接定位到出问题的那一行,增加了工作量。
Android开发模式:MVP Vs MVVM
三种框架的比较—教你认清MVC,MVP和MVVM -
工作中碰到的比较有价值的工作(或者碰到了哪些问题)