亲爱的面试官,这个我可没看过!(Android部分)

ButterKnife原理ButterKnife对性能的影响很小,因为没有使用使用反射,而是使用的AnnotationProcessingTool(APT),注解处理器,javac中用于编译时扫描和解析Java注解的工具。在编译阶段执行的,它的原理就是读入Java源代码,解析注解,然后生成新的Java代码。新生成的Java代码最后被编译成Java字节码,注解解析器不能改变读入的Java类,比如不能加入或删除Java方法。

Activity和Fragment生命周期有哪些?

横竖屏切换时候Activity的生命周期

不设置Activity的android:configChanges时,切屏会重新回掉各个生命周期,切横屏时会执行一次,切竖屏时会执行两次设置Activity的android:configChanges=”orientation”时,切屏还是会调用各个生命周期,切换横竖屏只会执行一次设置Activity的android:configChanges=”orientation|keyboardHidden”时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

关于默认线程池:核心线程池中最多有CPU_COUNT+1个,最多有CPU_COUNT*2+1个,线程等待队列的最大等待数为128,但是可以自定义线程池。线程池是由AsyncTask来管理的,线程池允许tasks并行运行,需要注意的是并发情况下数据的一致性问题,新数据可能会被老数据覆盖掉,类似volatile变量。所以希望tasks能够串行运行的话,使用SERIAL_EXECUTOR。

Acitivty的任务栈

使用android:launchMode="standard|singleInstance|singleTask|singleTop"来控制Acivity任务栈。

任务栈是一种后进先出的结构。位于栈顶的Activity处于焦点状态,当按下back按钮的时候,栈内的Activity会一个一个的出栈,并且调用其onDestory()方法。如果栈内没有Activity,那么系统就会回收这个栈,每个APP默认只有一个栈,以APP的包名来命名.

onSaveInstanceState()与onRestoreIntanceState()用户或者程序员主动去销毁一个Activity的时候不会掉用,其他情况都会调动,来保存界面信息。如代码中finish()或用户按下back,不会掉用。

android中进程的优先级?

Serializable和Parcelable序列化,表示将一个对象转换成可存储或可传输的状态。序列化后的对象可以在网络上进行传输,也可以存储到本地。

动画

计算属性分为3个过程:

Android的数据存储形式

Activity和Service以及Application的Context是不一样的,Activity继承自ContextThemeWraper.其他的继承自ContextWrapper.每一个Activity和Service以及Application的Context都是一个新的ContextImpl对象getApplication()用来获取Application实例的,但是这个方法只有在Activity和Service中才能调用的到。那么也许在绝大多数情况下我们都是在Activity或者Service中使用Application的,但是如果在一些其它的场景,比如BroadcastReceiver中也想获得Application的实例,这时就可以借助getApplicationContext()方法,getApplicationContext()比getApplication()方法的作用域会更广一些,任何一个Context的实例,只要调用getApplicationContext()方法都可以拿到我们的Application对象。创建Toast和对话框不可以用Application的context,只能用Activity的context。Context的数量等于Activity的个数+Service的个数+1,这个1为Application

Android各版本新特性Android5.0新特性

Android6.0新特性

Android7.0新特性

Json

对象可以包含多个名称/值对,比如:

{"name":"zhangsan","age":25}使用谷歌的GSON包进行解析在AndroidStudio里引入依赖:

compile'com.google.code.gson:gson:2.7'值得注意的是实体类中变量名称必须和json中的值名相同。json1的解析我们这里的实体类是Student.class

Gsongson=newGson();Studentstudent=gson.fromJson(json1,Student.class);json2的解析我们可以解析成int数组,也可以解析成Integer的List。解析成数组:

Gsongson=newGson();int[]ages=gson.fromJson(json2,int[].class);解析成List:Gsongson=newGson();Listages=gson.fromJson(json2,newTypeToken>(){}.getType);json3的解析同样可以解析成List或者数组,我们就直接解析成List.

Gsongson=newGson();Liststudents=gson.fromJson(json3,newTypeToke>(){}.getType);android中有哪几种解析xml的类,官方推荐哪种?以及它们的原理和区别

优点:1.XML树在内存中完整存储,因此可以直接修改其数据和结构.2.可以通过该解析器随时访问XML树中的任何一个节点.3.DOM解析器的API在使用上也相对比较简单.缺点:如果XML文档体积比较大时,将文档读入内存是非常消耗系统资源的.使用场景:DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点的集合.这个层次结构允许开发人员在树中寻找特定信息.分析该结构通常需要加载整个文档和构造层次结构,然后才能进行任何工作.DOM是基于对象层次结构的.

优点:SAX对内存的要求比较低,因为它让开发人员自己来决定所要处理的标签.特别是当开发人员只需要处理文档中所包含的部分数据时,SAX这种扩展能力得到了更好的体现.缺点:用SAX方式进行XML解析时,需要顺序执行,所以很难访问到同一文档中的不同数据.此外,在基于该方式的解析编码过程也相对复杂.使用场景:对于含有数据量十分巨大,而又不用对文档的所有数据进行遍历或者分析的时候,使用该方法十分有效.该方法不用将整个文档读入内存,而只需读取到程序所需的文档标签处即可.

androidSDK提供了xmlpullapi,xmlpull和sax类似,是基于流(stream)操作文件,然后根据节点事件回调开发者编写的处理程序.因为是基于流的处理,因此xmlpull和sax都比较节约内存资源,不会象dom那样要把所有节点以对橡树的形式展现在内存中.xmlpull比sax更简明,而且不需要扫描完整个流.

Jar和Aar的区别

Jar包里面只有代码,aar里面不光有代码还包括代码还包括资源文件,比如drawable文件,xml资源文件。对于一些不常变动的AndroidLibrary,我们可以直接引用aar,加快编译速度

什么是三级缓存(研究中)

三级缓存原理(研究中)

Android为每个应用程序分配的内存大小是多少android程序内存一般限制在16M,也有的是24M

更新UI方式Activity.runOnUiThread(Runnable)View.post(Runnable),View.postDelay(Runnable,long)HandlerAsyncTask

引起内存泄漏的情况

Activity/Window/View三者的差别,fragment的特点

Activity像一个工匠(控制单元),Window像窗户(承载模型),View像窗花(显示视图)LayoutInflater像剪刀,Xml配置像窗花图纸。

在Activity中调用attach,创建了一个Window创建的window是其子类PhoneWindow,在attach中创建PhoneWindow在Activity中调用setContentView(R.layout.xxx)其中实际上是调用的getWindow().setContentView()调用PhoneWindow中的setContentView方法创建ParentView:作为ViewGroup的子类,实际是创建的DecorView(作为FramLayout的子类)将指定的R.layout.xxx进行填充通过布局填充器进行填充【其中的parent指的就是DecorView】调用到ViewGroup调用ViewGroup的removeAllView(),先将所有的view移除掉添加新的view:addView()

Fragment特点Fragment可以作为Activity界面的一部分组成出现;可以在一个Activity中同时出现多个Fragment,并且一个Fragment也可以在多个Activity中使用;在Activity运行过程中,可以添加、移除或者替换Fragment;Fragment可以响应自己的输入事件,并且有自己的生命周期,它们的生命周期会受宿主Activity的生命周期影响。

JVM和Dalvik虚拟机的区别

怎么考虑数据传输的安全性如果应用对传输的数据没有任何安全措施,攻击者设置的钓鱼网络中更改DNS服务器。这台服务器可以获取用户信息,或充当中间人与原服务器交换数据。在SSL/TLS通信中,客户端通过数字证书判断服务器是否可信,并采用证书的公钥与服务器进行加密通信。

dispatchTouchEvent的执行顺序为:

首先触发ACTIVITY的dispatchTouchEvent,然后触发ACTIVITY的onInterceptTouchEvent.然后触发LAYOUT的dispatchTouchEvent,然后触发LAYOUT的onInterceptTouchEvent这就解释了重写ViewGroup时必须调用super.dispatchTouchEvent();

(1)dispatchTouchEvent:

此方法一般用于初步处理事件,因为动作是由此分发,所以通常会调用super.dispatchTouchEvent。这样就会继续调用onInterceptTouchEvent,再由onInterceptTouchEvent决定事件流向。

(2)onInterceptTouchEvent:

若返回值为true事件会传递到自己的onTouchEvent();若返回值为false传递到下一个View的dispatchTouchEvent();

(3)onTouchEvent():

若返回值为true,事件由自己消耗,后续动作让其处理;若返回值为false,自己不消耗事件了,向上返回让其他的父View的onTouchEvent接受处理

三大方法关系的伪代码:如果当前View拦截事件,就交给自己的onTouchEvent去处理,否则就丢给子View继续走相同的流程。

publicbooleandispatchTouchEvent(MotionEventev){booleanconsume=false;if(onInterceptTouchEvent(ev)){consume=onTouchEvent(ev);}else{consume=child.dispatchTouchEvent(ev);}returnconsume;}onTouchEvent的传递:

当有多个层级的View时,在父层级允许的情况下,这个action会一直传递直到遇到最深层的View。所以touch事件最先调用的是最底层View的onTouchEvent,如果View的onTouchEvent接收到某个touchaction并做了相应处理,最后有两种返回方式returntrue和returnfalse;returntrue会告诉系统当前的View需要处理这次的touch事件,以后的系统发出的ACTION_MOVE,ACTION_UP还是需要继续监听并接收的,并且这次的action已经被处理掉了,父层的View是不可能触发onTouchEvent的了。所以每一个action最多只能有一个onTouchEvent接口返回true。如果返回false,便会通知系统,当前View不关心这一次的touch事件,此时这个action会传向父级,调用父级View的onTouchEvent。但是这一次的touch事件之后发出任何action,该View都不在接受,onTouchEvent在这一次的touch事件中再也不会触发,也就是说一旦View返回false,那么之后的ACTION_MOVE,ACTION_UP等ACTION就不会在传入这个View,但是下一次touch事件的action还是会传进来的。

父层的onInterceptTouchEvent

前面说了底层的View能够接收到这次的事件有一个前提条件:在父层允许的情况下。假设不改变父层级的dispatch方法,在系统调用底层onTouchEvent之前会调用父View的onInterceptTouchEvent方法判断,父层View是否要截获本次touch事件之后的action。如果onInterceptTouchEvent返回了true,那么本次touch事件之后的所有action都不会向深层的View传递,统统都会传给父层View的onTouchEvent,就是说父层已经截获了这次touch事件,之后的action也不必询问onInterceptTouchEvent,在这次的touch事件之后发出的action时onInterceptTouchEvent不会再被调用,直到下一次touch事件的来临。如果onInterceptTouchEvent返回false,那么本次action将发送给更深层的View,并且之后的每一次action都会询问父层的onInterceptTouchEvent需不需要截获本次touch事件。只有ViewGroup才有onInterceptTouchEvent方法,因为一个普通的View肯定是位于最深层的View,touch能够传到这里已经是最后一站了,肯定会调用View的onTouchEvent()。

底层View的getParent().requestDisallowInterceptTouchEvent(true)

对于底层的View来说,有一种方法可以阻止父层的View获取touch事件,就是调用getParent().requestDisallowInterceptTouchEvent(true)方法。一旦底层View收到touch的action后调用这个方法那么父层View就不会再调用onInterceptTouchEvent了,也无法截获以后的action(如果父层ViewGroup和最底层View需要截获不同焦点,或不同手势的touch,不能使用这个写死)。

ART和Dalvik区别

ART:AheadofTimeDalvik:JustinTime

什么是Dalvik:Dalvik是Google公司自己设计用于Android平台的Java虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一,它可以支持已转换为.dex(即DalvikExecutable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

ART优点:

系统性能的显著提升应用启动更快、运行更快、体验更流畅、触感反馈更及时。更长的电池续航能力支持更低的硬件

Scroller原理

Scroller执行流程里面的三个核心方法

webView.addJavaScriptInterface(newObject(){xxx},"xxx");1答案:可以使用WebView控件执行JavaScript脚本,并且可以在JavaScript中执行Java代码。要想让WebView控件执行JavaScript,需要调用WebSettings.setJavaScriptEnabled方法,代码如下:WebViewwebView=(WebView)findViewById(R.id.webview);WebSettingswebSettings=webView.getSettings();//设置WebView支持JavaScriptwebSettings.setJavaScriptEnabled(true);webView.setWebChromeClient(newWebChromeClient());JavaScript调用Java方法需要使用WebView.addJavascriptInterface方法设置JavaScript调用的Java方法,代码如下:webView.addJavascriptInterface(newObject(){//JavaScript调用的方法publicStringprocess(Stringvalue){//处理代码returnresult;}},"demo");//demo是Java对象映射到JavaScript中的对象名可以使用下面的JavaScript代码调用process方法,代码如下:functionsearch(){//调用searchWord方法result.innerHTML=""+window.demo.process('data')+"";}SurfaceView和View的最本质的区别

SurfaceView是在一个新起的单独线程中可以重新绘制画面,而view必须在UI的主线程中更新画面。

单例publicclassSingleton{privatevolatilestaticSingletonmSingleton;privateSingleton(){}publicstaticSingletongetInstance(){if(mSingleton==null){\\Asynchronized(Singleton.class){\\Cif(mSingleton==null)mSingleton=newSingleton();\\B}}returnmSingleton;}}ANR排错

1、ANR排错一般有三种情况

2、如何避免

3、ANR定位和修正如果开发机器上出现问题,我们可以通过查看/data/anr/traces.txt即可,最新的ANR信息在最开始部分。

Android程序运行时权限与文件系统权限1,Linux文件系统权限。不同的用户对文件有不同的读写执行权限。在android系统中,system和应用程序是分开的,system里的数据是不可更改的。2,Android中有3种权限,进程权限UserID,签名,应用申明权限。每次安装时,系统根据包名为应用分配唯一的userID,不同的userID运行在不同的进程里,进程间的内存是独立的,不可以相互访问,除非通过特定的Binder机制。Android提供了如下的一种机制,可以使两个apk打破前面讲的这种壁垒。在AndroidManifest.xml中利用sharedUserId属性给不同的package分配相同的userID,通过这样做,两个package可以被当做同一个程序,系统会分配给两个程序相同的UserID。当然,基于安全考虑,两个package需要有相同的签名,否则没有验证也就没有意义了。

如何让程序自动启动定义一个Braodcastreceiver,action为BOOT——COMPLETE,接受到广播后启动程序。

Handler机制andriod提供了Handler和Looper来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。Looper:一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue(消息队列)。Handler:你可以构造Handler对象来与Looper沟通,以便push新消息到MessageQueue里;或者接收Looper从MessageQueue取出)所送来的消息。MessageQueue(消息队列):用来存放线程放入的消息。线程:UIthread通常就是mainthread,而Android启动程序时会替它建立一个MessageQueue。

ListView卡顿原因Adapter的getView方法里面convertView没有使用setTag和getTag方式;在getView方法里面ViewHolder初始化后的赋值或者是多个控件的显示状态和背景的显示没有优化好,抑或是里面含有复杂的计算和耗时操作;在getView方法里面inflate的row嵌套太深(布局过于复杂)或者是布局里面有大图片或者背景所致;Adapter多余或者不合理的notifySetDataChanged;listview被多层嵌套,多次的onMessure导致卡顿,如果多层嵌套无法避免,建议把listview的高和宽设置为fill_parent.如果是代码继承的listview,那么也请你别忘记为你的继承类添加上LayoutPrams,注意高和宽都是fill_parent的;

启动一个程序,可以主界面点击图标进入,也可以从一个程序中跳转过去,二者有什么区别?是因为启动程序(主界面也是一个app),发现了在这个程序中存在一个设置为

的activity,所以这个launcher会把icon提出来,放在主界面上。当用户点击icon的时候,发出一个Intent:

Intentintent=mActivity.getPackageManager().getLaunchIntentForPackage(packageName);mActivity.startActivity(intent);跳过去可以跳到任意允许的页面,如一个程序可以下载,那么真正下载的页面可能不是首页(也有可能是首页),这时还是构造一个Intent,startActivity.这个intent中的action可能有多种view,download都有可能。系统会根据第三方程序向系统注册的功能,为你的Intent选择可以打开的程序或者页面。所以唯一的一点不同的是从icon的点击启动的intent的action是相对单一的,从程序中跳转或者启动可能样式更多一些。本质是相同的。

aidl主要就是帮助我们完成了包装数据和解包的过程,并调用了transact过程,而用来传递的数据包我们就称为parcel

AIDL:xxx.aidl->xxx.java,注册service

用aidl定义需要被调用方法接口实现这些方法调用这些方法

FC(ForceClose)

什么时候会出现

解决的办法

界面优化太多重叠的背景(overdraw)

这个问题其实最容易解决,建议就是检查你在布局和代码中设置的背景,有些背景是隐藏在底下的,它永远不可能显示出来,这种没必要的背景一定要移除,因为它很可能会严重影响到app的性能。如果采用的是selector的背景,将normal状态的color设置为”@android:color/transparent”,也同样可以解决问题。

太多重叠的View

第二个建议是:如果使用了类似Viewpager+Fragment这样的组合或者有多个Fragment在一个界面上,需要控制Fragment的显示和隐藏,尽量使用动态的Inflationview,它的性能要比SetVisibility好。

复杂的Layout层级

这里的建议比较多一些,首先推荐使用Android提供的布局工具HierarchyViewer来检查和优化布局。第一个建议是:如果嵌套的线性布局加深了布局层次,可以使用相对布局来取代。第二个建议是:用标签来合并布局。第三个建议是:用标签来重用布局,抽取通用的布局可以让布局的逻辑更清晰明了。记住,这些建议的最终目的都是使得你的Layout在HierarchyViewer里变得宽而浅,而不是窄而深。

总结:可以考虑多使用merge和include,ViewStub。尽量使布局浅平,根布局尽量少使用RelactivityLayout,因为RelactivityLayout每次需要测量2次。

移动端获取网络数据优化的几个点

请求合并:即将多个请求合并为一个进行请求,比较常见的就是网页中的CSSImageSprites。如果某个页面内请求过多,也可以考虑做一定的请求合并。

Android系统启动过程,App启动过程]从桌面点击到activity启动的过程

1.Launcher线程捕获onclick的点击事件,调用Launcher.startActivitySafely,进一步调用Launcher.startActivity,最后调用父类Activity的startActivity。

2.Activity和ActivityManagerService交互,引入Instrumentation,将启动请求交给Instrumentation,调用Instrumentation.execStartActivity。

3.调用ActivityManagerService的startActivity方法,这里做了进程切换(具体过程请查看源码)。

4.开启Activity,调用onCreate方法

热布丁原因:因为一个dvm中存储方法id用的是short类型,导致dex中方法不能超过65536个原理:将编译好的class文件拆分打包成两个dex,绕过dex方法数量的限制以及安装时的检查,在运行时再动态加载第二个dex文件中。使用Dexclassloader。

动态加载(也叫插件化技术)动态加载主要解决3个技术问题:1,使用ClassLoader加载类。2,资源访问。3,生命周期管理。

Binder机制

跨进程间通信(IPC):四大组件之间通过Intent互相跳转,Android实现IPC的方式是binder机制。

THE END
1.面试技术点之安卓篇A HandSome Man: 这个写法有问题啊,首次点击直接是fasle flutter 自定义组件-抽奖大转盘 lostcixin: luck_util这个类文件能否也发一下? 最新文章 Android中Gradle常用配置 面试技术点之kotlin篇 面试技术点之flutter篇 2024年20篇 2023年1篇 2022年7篇 2021年26篇 2020年21篇 2019年15篇 2018年29篇 2017年https://blog.csdn.net/NotesChapter/article/details/144350679
2.Android和iOS平台特性mob64ca12f5c08e的技术博客Android与iOS平台特性分析 在移动应用开发的世界中,Android和iOS是两个主流的平台,它们各自有着独特的特性和优势。了解这些特性对于开发者而言至关重要,可以帮助他们在不同平台上更好地实现应用功能。 1. 平台架构 Android Android是一个开放源代码的平台,运行在Java虚拟机(JVM)上。它的应用是基于Android SDK开发的,https://blog.51cto.com/u_16213454/11873550
3.笔记(24)安卓各版本特性简述腾讯云开发者社区4.4 特性 1.webview与js交换可以使用@JavascriptInterface注释接口方法,通过webveiw向js中注入对象调用该方法,修复4.4以下安全漏洞风险 5.0 新特性—2014年(Lollipop) 1.全新的Material Design设计风格 2.支持64位ART虚拟机。 1.放弃了之前一直使用的Dalvik虚拟机,改用了ART虚拟机,实现了真正的跨平台编译。Android内存https://cloud.tencent.com/developer/article/2368979
4.什么是鸿蒙系统?一文快速了解鸿蒙系统基础知识2.鸿蒙系统与Android有何不同? 鸿蒙系统(HarmonyOS),由华为公司开发,是一种面向智能终端的分布式操作系统。而Android是由Google公司开发的一种基于Linux内核的开源移动操作系统。 1. 设计理念不同 鸿蒙系统:鸿蒙系统的设计理念是构建一个统一的、无缝的分布式操作系统。它支持多种设备之间的协同工作,并提供一致的用户https://www.eefocus.com/e/1593577.html
5.usdt钱包软件有哪些?十大usdt钱包app汇总交易平台区块链imToken冷钱包 v2.14.1 Android & Ios最新版 类型:银行金融 大小:46.18MB 语言:简体中文 时间:2024-02-04 查看详情 imToken钱包优势 多链钱包,轻松使用 一套助记词管理多链钱包,告别繁复备份 多链各有差异,支付体验同样流畅 代币兑换,安全快速 私钥自持,无需信任第三方即可完成操作 https://www.jb51.net/blockchain/877588.html
6.诸神皇冠国际服下载亡人节有三种祈祷,祈祷受伤角色痊愈,祈祷已婚人士生孩子,祈祷延寿,祈祷延寿暂时无用,其他两种成功率看脸。 亡人节可以通过拜神治愈伤痕(后天负面特性消除,先天负面不能消除),延长寿命(表现为不会衰老,但还是会退休),提高怀孕几率(先天绝育无法提高 ),一样要求在自家城堡神庙内修建相关神像 玩家热评 玩家1: https://shouyou.3dmgame.com/android/140612.html
7.文档中心以下为一些常见的技术问题解答及常见概念的理解,本文档将持续补充,对推送有疑问的开发者可以自行 Ctrl+F 搜索关键字找寻解答 一、概况问题 1. 如何申请接入? 感谢您选择小米推送,点此查看接入指南; 2. 小米推送收费吗? 小米推送的基础服务目前是免费的。 3. 小米推送目前支持哪些平台? 目前仅支持Android平台。https://dev.mi.com/console/doc/detail?pId=1292
8.vivo开放平台每次Android系统的升级,几乎都会在交互体验上带来一些新变化,此次Android 11也不例外。 例如: 2.2.1 聊天气泡 聊天气泡(chat bubbles)的会话方式正式推出,不再是实验性功能,该特性支持会话窗口最小化为一个或多个悬浮气泡,气泡可以展开为小窗口模式的页面; https://dev.vivo.com.cn/wap/documentCenter/doc/428
9.OPPO开放平台本文档基于谷歌Android 11 Developer Preview 4(DP4)版本的变更输出,后续Beta版如有新的变更和特性,我们会刷新文档的相关章节内容,请开发者持续关注。 一、兼容性调试工具 Android 11 引入了新的工具,用于针对最新版平台中的行为变更来测试和调试应用。这些工具属于新的兼容性框架的一部分,可让应用开发者单独开启和关https://open.oppomobile.com/new/developmentDoc/info?id=10724
10.ifanr访谈:友盟,不只是数据服务平台爱范儿核心业务:数据服务平台 ifanr:友盟主要的业务是在数据这一块,外界所了解的友盟,也是一个数据服务平台。有什么运营数据可以分享吗? 蒋凡:我们平台里现在有 2 万多款应用,每天活跃用户(DAU)在 4000 万左右。全球(有一些国外用户)覆盖了 1.6 亿部设备,国内占了 9000 万– 1 亿。Android 平台 6000 台设备,iOS https://www.ifanr.com/76731/
11.15款能够替代Notion的优秀软件(都有免费版)有很多优秀的产品都可以代替 Notion 来做笔记和做项目管理,而且这些替代产品的价格比 Notion 更加实惠。 神译局是36氪旗下编译团队,关注科技、商业、职场、生活等领域,重点介绍国外的新技术、新观点、新风向。 编者按:Notion 是一个公认的强大的笔记和项目管理平台,但功能的强大来自于极其复杂的定制,并不是所有人都https://36kr.com/p/2291085667260169
12.OSCHINA开源的 Linux 游戏平台「GitHub 热点速览」 977阅 告别头文件,编译效率提升 42%!C++ Modules 实战解析 | 干货推荐 569阅 云计算专区 169.6万人关注 Coolbpf 最新特性系列解读:eNetSTL 网络功能加速库在龙蜥社区开源 359阅 揭秘Zadig 多环境配置最佳实践 - Helm Chart 篇 https://www.oschina.net/